2015-01-16 22:38:38 +00:00
|
|
|
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
2015-10-06 13:53:35 +00:00
|
|
|
-- Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org>
|
2015-01-16 22:38:38 +00:00
|
|
|
-- Licensed to the public under the Apache License 2.0.
|
2008-07-29 20:32:02 +00:00
|
|
|
|
2009-07-19 00:24:58 +00:00
|
|
|
local fs = require "nixio.fs"
|
2008-08-29 23:26:01 +00:00
|
|
|
local sys = require "luci.sys"
|
|
|
|
local util = require "luci.util"
|
|
|
|
local http = require "luci.http"
|
2009-06-21 13:42:26 +00:00
|
|
|
local nixio = require "nixio", require "nixio.util"
|
2008-03-02 21:52:58 +00:00
|
|
|
|
2008-08-29 23:26:01 +00:00
|
|
|
module("luci.dispatcher", package.seeall)
|
2008-11-29 21:58:39 +00:00
|
|
|
context = util.threadlocal()
|
2009-11-10 16:02:48 +00:00
|
|
|
uci = require "luci.model.uci"
|
2009-11-29 13:46:04 +00:00
|
|
|
i18n = require "luci.i18n"
|
2009-11-14 18:41:16 +00:00
|
|
|
_M.fs = fs
|
2008-03-29 18:22:21 +00:00
|
|
|
|
2008-05-26 12:16:16 +00:00
|
|
|
-- Index table
|
2008-06-14 14:12:12 +00:00
|
|
|
local index = nil
|
2008-05-26 12:16:16 +00:00
|
|
|
|
2008-06-02 15:36:13 +00:00
|
|
|
-- Fastindex
|
|
|
|
local fi
|
|
|
|
|
2008-05-05 19:27:30 +00:00
|
|
|
|
2008-05-29 13:51:32 +00:00
|
|
|
function build_url(...)
|
2008-12-15 10:40:45 +00:00
|
|
|
local path = {...}
|
2010-11-13 13:50:54 +00:00
|
|
|
local url = { http.getenv("SCRIPT_NAME") or "" }
|
|
|
|
|
|
|
|
local p
|
|
|
|
for _, p in ipairs(path) do
|
2010-11-13 20:50:20 +00:00
|
|
|
if p:match("^[a-zA-Z0-9_%-%.%%/,;]+$") then
|
2010-11-13 13:50:54 +00:00
|
|
|
url[#url+1] = "/"
|
|
|
|
url[#url+1] = p
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-10-21 14:45:48 +00:00
|
|
|
if #path == 0 then
|
|
|
|
url[#url+1] = "/"
|
|
|
|
end
|
|
|
|
|
2010-11-13 13:50:54 +00:00
|
|
|
return table.concat(url, "")
|
2008-03-02 21:52:58 +00:00
|
|
|
end
|
|
|
|
|
2018-11-27 14:23:41 +00:00
|
|
|
function _ordered_children(node)
|
|
|
|
local name, child, children = nil, nil, {}
|
|
|
|
|
|
|
|
for name, child in pairs(node.nodes) do
|
|
|
|
children[#children+1] = {
|
|
|
|
name = name,
|
|
|
|
node = child,
|
|
|
|
order = child.order or 100
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
table.sort(children, function(a, b)
|
|
|
|
if a.order == b.order then
|
|
|
|
return a.name < b.name
|
|
|
|
else
|
|
|
|
return a.order < b.order
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
return children
|
|
|
|
end
|
|
|
|
|
2019-10-31 17:25:23 +00:00
|
|
|
local function dependencies_satisfied(node)
|
|
|
|
if type(node.file_depends) == "table" then
|
|
|
|
for _, file in ipairs(node.file_depends) do
|
|
|
|
local ftype = fs.stat(file, "type")
|
|
|
|
if ftype == "dir" then
|
|
|
|
local empty = true
|
|
|
|
for e in (fs.dir(file) or function() end) do
|
|
|
|
empty = false
|
|
|
|
end
|
|
|
|
if empty then
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
elseif ftype == nil then
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if type(node.uci_depends) == "table" then
|
|
|
|
for config, expect_sections in pairs(node.uci_depends) do
|
|
|
|
if type(expect_sections) == "table" then
|
|
|
|
for section, expect_options in pairs(expect_sections) do
|
|
|
|
if type(expect_options) == "table" then
|
|
|
|
for option, expect_value in pairs(expect_options) do
|
|
|
|
local val = uci:get(config, section, option)
|
|
|
|
if expect_value == true and val == nil then
|
|
|
|
return false
|
|
|
|
elseif type(expect_value) == "string" then
|
|
|
|
if type(val) == "table" then
|
|
|
|
local found = false
|
|
|
|
for _, subval in ipairs(val) do
|
|
|
|
if subval == expect_value then
|
|
|
|
found = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if not found then
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
elseif val ~= expect_value then
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
else
|
|
|
|
local val = uci:get(config, section)
|
|
|
|
if expect_options == true and val == nil then
|
|
|
|
return false
|
|
|
|
elseif type(expect_options) == "string" and val ~= expect_options then
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
elseif expect_sections == true then
|
|
|
|
if not uci:get_first(config) then
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
2011-10-26 00:24:17 +00:00
|
|
|
function node_visible(node)
|
|
|
|
if node then
|
|
|
|
return not (
|
2019-10-31 17:25:23 +00:00
|
|
|
(not dependencies_satisfied(node)) or
|
2011-10-26 00:24:17 +00:00
|
|
|
(not node.title or #node.title == 0) or
|
|
|
|
(not node.target or node.hidden == true) or
|
|
|
|
(type(node.target) == "table" and node.target.type == "firstchild" and
|
|
|
|
(type(node.nodes) ~= "table" or not next(node.nodes)))
|
|
|
|
)
|
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
function node_childs(node)
|
2011-10-26 03:04:41 +00:00
|
|
|
local rv = { }
|
|
|
|
if node then
|
2018-11-27 14:23:41 +00:00
|
|
|
local _, child
|
|
|
|
for _, child in ipairs(_ordered_children(node)) do
|
|
|
|
if node_visible(child.node) then
|
|
|
|
rv[#rv+1] = child.name
|
2011-10-26 03:04:41 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return rv
|
2011-10-26 00:24:17 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
2008-03-02 21:52:58 +00:00
|
|
|
function error404(message)
|
2015-01-15 15:32:03 +00:00
|
|
|
http.status(404, "Not Found")
|
2008-03-02 21:52:58 +00:00
|
|
|
message = message or "Not Found"
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2018-04-10 10:03:15 +00:00
|
|
|
local function render()
|
|
|
|
local template = require "luci.template"
|
|
|
|
template.render("error404")
|
|
|
|
end
|
|
|
|
|
|
|
|
if not util.copcall(render) then
|
2015-01-15 15:32:03 +00:00
|
|
|
http.prepare_content("text/plain")
|
|
|
|
http.write(message)
|
2008-03-02 21:52:58 +00:00
|
|
|
end
|
2018-04-10 10:03:15 +00:00
|
|
|
|
2008-05-24 22:58:45 +00:00
|
|
|
return false
|
2008-03-02 21:52:58 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function error500(message)
|
2015-01-15 15:32:03 +00:00
|
|
|
util.perror(message)
|
2009-02-09 13:17:26 +00:00
|
|
|
if not context.template_header_sent then
|
2015-01-15 15:32:03 +00:00
|
|
|
http.status(500, "Internal Server Error")
|
|
|
|
http.prepare_content("text/plain")
|
|
|
|
http.write(message)
|
2009-02-09 13:17:26 +00:00
|
|
|
else
|
|
|
|
require("luci.template")
|
2015-01-15 15:32:03 +00:00
|
|
|
if not util.copcall(luci.template.render, "error500", {message=message}) then
|
|
|
|
http.prepare_content("text/plain")
|
|
|
|
http.write(message)
|
2009-02-09 13:17:26 +00:00
|
|
|
end
|
2008-03-02 21:52:58 +00:00
|
|
|
end
|
2008-05-24 22:58:45 +00:00
|
|
|
return false
|
2008-03-02 21:52:58 +00:00
|
|
|
end
|
|
|
|
|
2009-07-25 12:45:38 +00:00
|
|
|
function httpdispatch(request, prefix)
|
2015-01-15 15:32:03 +00:00
|
|
|
http.context.request = request
|
2009-07-25 12:45:38 +00:00
|
|
|
|
|
|
|
local r = {}
|
|
|
|
context.request = r
|
2011-10-26 00:24:17 +00:00
|
|
|
|
2009-01-20 19:40:14 +00:00
|
|
|
local pathinfo = http.urldecode(request:getenv("PATH_INFO") or "", true)
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2009-07-25 12:45:38 +00:00
|
|
|
if prefix then
|
|
|
|
for _, node in ipairs(prefix) do
|
|
|
|
r[#r+1] = node
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-04-10 09:38:29 +00:00
|
|
|
local node
|
|
|
|
for node in pathinfo:gmatch("[^/%z]+") do
|
2015-10-20 22:31:27 +00:00
|
|
|
r[#r+1] = node
|
2008-03-02 21:52:58 +00:00
|
|
|
end
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2009-02-26 16:45:01 +00:00
|
|
|
local stat, err = util.coxpcall(function()
|
2009-07-24 15:45:29 +00:00
|
|
|
dispatch(context.request)
|
2009-02-26 16:45:01 +00:00
|
|
|
end, error500)
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2015-01-15 15:32:03 +00:00
|
|
|
http.close()
|
2008-09-05 20:32:20 +00:00
|
|
|
|
|
|
|
--context._disable_memtrace()
|
2008-05-04 20:53:31 +00:00
|
|
|
end
|
|
|
|
|
2019-10-09 07:55:44 +00:00
|
|
|
local function require_post_security(target, args)
|
|
|
|
if type(target) == "table" and target.type == "arcombine" and type(target.targets) == "table" then
|
|
|
|
return require_post_security((type(args) == "table" and #args > 0) and target.targets[2] or target.targets[1], args)
|
|
|
|
end
|
|
|
|
|
2015-10-20 18:58:30 +00:00
|
|
|
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
|
2018-04-09 05:04:38 +00:00
|
|
|
(required_val == true and request_val == nil)
|
2015-10-20 18:58:30 +00:00
|
|
|
then
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
return (target.post == true)
|
|
|
|
end
|
|
|
|
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
2015-10-22 06:30:29 +00:00
|
|
|
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
|
|
|
|
|
2017-07-09 19:26:49 +00:00
|
|
|
local function session_retrieve(sid, allowed_users)
|
|
|
|
local sdat = util.ubus("session", "get", { ubus_rpc_session = sid })
|
|
|
|
|
|
|
|
if type(sdat) == "table" and
|
|
|
|
type(sdat.values) == "table" and
|
|
|
|
type(sdat.values.token) == "string" and
|
2017-07-11 12:12:50 +00:00
|
|
|
(not allowed_users or
|
|
|
|
util.contains(allowed_users, sdat.values.username))
|
2017-07-09 19:26:49 +00:00
|
|
|
then
|
2018-04-24 19:26:01 +00:00
|
|
|
uci:set_session_id(sid)
|
2017-07-09 19:26:49 +00:00
|
|
|
return sid, sdat.values
|
|
|
|
end
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
end
|
|
|
|
|
2017-07-11 12:12:50 +00:00
|
|
|
local function session_setup(user, pass, allowed_users)
|
|
|
|
if util.contains(allowed_users, user) then
|
|
|
|
local login = util.ubus("session", "login", {
|
|
|
|
username = user,
|
|
|
|
password = pass,
|
|
|
|
timeout = tonumber(luci.config.sauth.sessiontime)
|
2017-07-09 19:26:49 +00:00
|
|
|
})
|
|
|
|
|
2018-01-17 17:49:08 +00:00
|
|
|
local rp = context.requestpath
|
|
|
|
and table.concat(context.requestpath, "/") or ""
|
|
|
|
|
2017-07-11 12:12:50 +00:00
|
|
|
if type(login) == "table" and
|
|
|
|
type(login.ubus_rpc_session) == "string"
|
|
|
|
then
|
|
|
|
util.ubus("session", "set", {
|
|
|
|
ubus_rpc_session = login.ubus_rpc_session,
|
|
|
|
values = { token = sys.uniqueid(16) }
|
|
|
|
})
|
|
|
|
|
2018-01-17 17:49:08 +00:00
|
|
|
io.stderr:write("luci: accepted login on /%s for %s from %s\n"
|
|
|
|
%{ rp, user, http.getenv("REMOTE_ADDR") or "?" })
|
|
|
|
|
2017-07-11 12:12:50 +00:00
|
|
|
return session_retrieve(login.ubus_rpc_session)
|
|
|
|
end
|
2018-01-17 17:49:08 +00:00
|
|
|
|
|
|
|
io.stderr:write("luci: failed login on /%s for %s from %s\n"
|
|
|
|
%{ rp, user, http.getenv("REMOTE_ADDR") or "?" })
|
2017-07-09 19:26:49 +00:00
|
|
|
end
|
|
|
|
|
2017-07-11 12:12:50 +00:00
|
|
|
return nil, nil
|
2017-07-09 19:26:49 +00:00
|
|
|
end
|
|
|
|
|
2009-07-24 15:45:29 +00:00
|
|
|
function dispatch(request)
|
2009-07-23 11:32:22 +00:00
|
|
|
--context._disable_memtrace = require "luci.debug".trap_memtrace("l")
|
2008-08-29 23:26:01 +00:00
|
|
|
local ctx = context
|
|
|
|
ctx.path = request
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2009-01-14 23:47:56 +00:00
|
|
|
local conf = require "luci.config"
|
2009-03-14 01:16:03 +00:00
|
|
|
assert(conf.main,
|
|
|
|
"/etc/config/luci seems to be corrupt, unable to find section 'main'")
|
|
|
|
|
2016-03-01 16:25:55 +00:00
|
|
|
local i18n = require "luci.i18n"
|
2010-04-16 19:05:05 +00:00
|
|
|
local lang = conf.main.lang or "auto"
|
2009-01-14 23:47:56 +00:00
|
|
|
if lang == "auto" then
|
|
|
|
local aclang = http.getenv("HTTP_ACCEPT_LANGUAGE") or ""
|
2017-08-13 13:54:01 +00:00
|
|
|
for aclang in aclang:gmatch("[%w_-]+") do
|
|
|
|
local country, culture = aclang:match("^([a-z][a-z])[_-]([a-zA-Z][a-zA-Z])$")
|
|
|
|
if country and culture then
|
|
|
|
local cc = "%s_%s" %{ country, culture:lower() }
|
|
|
|
if conf.languages[cc] then
|
|
|
|
lang = cc
|
|
|
|
break
|
|
|
|
elseif conf.languages[country] then
|
|
|
|
lang = country
|
|
|
|
break
|
|
|
|
end
|
|
|
|
elseif conf.languages[aclang] then
|
|
|
|
lang = aclang
|
2009-01-14 23:47:56 +00:00
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
2010-04-16 19:05:05 +00:00
|
|
|
end
|
2016-03-01 16:25:55 +00:00
|
|
|
if lang == "auto" then
|
|
|
|
lang = i18n.default
|
|
|
|
end
|
|
|
|
i18n.setlanguage(lang)
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2009-07-23 03:25:27 +00:00
|
|
|
local c = ctx.tree
|
|
|
|
local stat
|
2009-07-24 15:45:29 +00:00
|
|
|
if not c then
|
|
|
|
c = createtree()
|
|
|
|
end
|
|
|
|
|
2008-05-22 14:04:03 +00:00
|
|
|
local track = {}
|
2008-08-07 19:03:25 +00:00
|
|
|
local args = {}
|
2008-11-01 18:49:41 +00:00
|
|
|
ctx.args = args
|
|
|
|
ctx.requestargs = ctx.requestargs or args
|
2008-08-07 19:03:25 +00:00
|
|
|
local n
|
2008-12-14 21:43:10 +00:00
|
|
|
local preq = {}
|
2009-01-04 15:45:57 +00:00
|
|
|
local freq = {}
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2008-05-22 14:04:03 +00:00
|
|
|
for i, s in ipairs(request) do
|
[PATCH] Allow smarter node creation based on visibility during createtree
As I've brought up on the mailing list thread "High latency caused by full tree creation", there is a large amount of delay per LuCI request which is spent building the node tree in createtree(). Most nodes created aren't
needed for the view presented to the user and only serve to consume memory and CPU time during a page load.
My idea is to provide an easy mechanism for index()ers to determine which needs to be created and what isn't. Due to the constraints of the standard LuCI web interface, this optimization needs to establish a few rules:
* The page requested must have its node created
* All parents of the page being requested must be created, so the children inherit the track
* All the top-level nodes "Status", "System", "Services", "Network" (and others added by extensions) must be created in order to have their top-level tabs in the UI
* All peers of second-level nodes need to be created as well for the same reason, to display their links on the subindexes
To make this easy to implement in each controller, the attached patch adds an "inreq" field to each created node to indicate if it lies on the request path. To satisfy the "top level node" requirement, we always
add the top level node, then check its inreq property if the top-level node is not "in request", then the controller can exit index() early.
2011-08-12 11:05:59 +00:00
|
|
|
preq[#preq+1] = s
|
|
|
|
freq[#freq+1] = s
|
|
|
|
c = c.nodes[s]
|
|
|
|
n = i
|
|
|
|
if not c then
|
|
|
|
break
|
2008-05-24 22:58:45 +00:00
|
|
|
end
|
|
|
|
|
[PATCH] Allow smarter node creation based on visibility during createtree
As I've brought up on the mailing list thread "High latency caused by full tree creation", there is a large amount of delay per LuCI request which is spent building the node tree in createtree(). Most nodes created aren't
needed for the view presented to the user and only serve to consume memory and CPU time during a page load.
My idea is to provide an easy mechanism for index()ers to determine which needs to be created and what isn't. Due to the constraints of the standard LuCI web interface, this optimization needs to establish a few rules:
* The page requested must have its node created
* All parents of the page being requested must be created, so the children inherit the track
* All the top-level nodes "Status", "System", "Services", "Network" (and others added by extensions) must be created in order to have their top-level tabs in the UI
* All peers of second-level nodes need to be created as well for the same reason, to display their links on the subindexes
To make this easy to implement in each controller, the attached patch adds an "inreq" field to each created node to indicate if it lies on the request path. To satisfy the "top level node" requirement, we always
add the top level node, then check its inreq property if the top-level node is not "in request", then the controller can exit index() early.
2011-08-12 11:05:59 +00:00
|
|
|
util.update(track, c)
|
2008-09-23 00:10:51 +00:00
|
|
|
|
[PATCH] Allow smarter node creation based on visibility during createtree
As I've brought up on the mailing list thread "High latency caused by full tree creation", there is a large amount of delay per LuCI request which is spent building the node tree in createtree(). Most nodes created aren't
needed for the view presented to the user and only serve to consume memory and CPU time during a page load.
My idea is to provide an easy mechanism for index()ers to determine which needs to be created and what isn't. Due to the constraints of the standard LuCI web interface, this optimization needs to establish a few rules:
* The page requested must have its node created
* All parents of the page being requested must be created, so the children inherit the track
* All the top-level nodes "Status", "System", "Services", "Network" (and others added by extensions) must be created in order to have their top-level tabs in the UI
* All peers of second-level nodes need to be created as well for the same reason, to display their links on the subindexes
To make this easy to implement in each controller, the attached patch adds an "inreq" field to each created node to indicate if it lies on the request path. To satisfy the "top level node" requirement, we always
add the top level node, then check its inreq property if the top-level node is not "in request", then the controller can exit index() early.
2011-08-12 11:05:59 +00:00
|
|
|
if c.leaf then
|
|
|
|
break
|
2008-08-22 21:52:36 +00:00
|
|
|
end
|
2008-05-04 20:53:31 +00:00
|
|
|
end
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2008-08-07 19:03:25 +00:00
|
|
|
if c and c.leaf then
|
|
|
|
for j=n+1, #request do
|
2009-01-04 15:45:57 +00:00
|
|
|
args[#args+1] = request[j]
|
|
|
|
freq[#freq+1] = request[j]
|
2008-08-07 19:03:25 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-09-11 10:46:06 +00:00
|
|
|
ctx.requestpath = ctx.requestpath or freq
|
2008-12-14 21:43:10 +00:00
|
|
|
ctx.path = preq
|
|
|
|
|
2008-05-26 09:45:12 +00:00
|
|
|
-- Init template engine
|
2008-10-10 14:37:53 +00:00
|
|
|
if (c and c.index) or not track.notemplate then
|
2008-08-29 23:26:01 +00:00
|
|
|
local tpl = require("luci.template")
|
2008-10-19 20:49:10 +00:00
|
|
|
local media = track.mediaurlbase or luci.config.main.mediaurlbase
|
2009-08-07 15:16:14 +00:00
|
|
|
if not pcall(tpl.Template, "themes/%s/header" % fs.basename(media)) then
|
2008-09-29 15:38:13 +00:00
|
|
|
media = nil
|
|
|
|
for name, theme in pairs(luci.config.themes) do
|
|
|
|
if name:sub(1,1) ~= "." and pcall(tpl.Template,
|
|
|
|
"themes/%s/header" % fs.basename(theme)) then
|
|
|
|
media = theme
|
|
|
|
end
|
|
|
|
end
|
|
|
|
assert(media, "No valid theme found")
|
|
|
|
end
|
|
|
|
|
2019-02-12 07:28:21 +00:00
|
|
|
local function _ifattr(cond, key, val, noescape)
|
2011-10-26 02:17:45 +00:00
|
|
|
if cond then
|
2011-10-30 15:00:54 +00:00
|
|
|
local env = getfenv(3)
|
|
|
|
local scope = (type(env.self) == "table") and env.self
|
2016-02-10 18:30:43 +00:00
|
|
|
if type(val) == "table" then
|
|
|
|
if not next(val) then
|
|
|
|
return ''
|
|
|
|
else
|
|
|
|
val = util.serialize_json(val)
|
|
|
|
end
|
|
|
|
end
|
2019-02-12 07:28:21 +00:00
|
|
|
|
|
|
|
val = tostring(val or
|
|
|
|
(type(env[key]) ~= "function" and env[key]) or
|
|
|
|
(scope and type(scope[key]) ~= "function" and scope[key]) or "")
|
|
|
|
|
|
|
|
if noescape ~= true then
|
|
|
|
val = util.pcdata(val)
|
|
|
|
end
|
|
|
|
|
|
|
|
return string.format(' %s="%s"', tostring(key), val)
|
2011-10-26 02:17:45 +00:00
|
|
|
else
|
|
|
|
return ''
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-07-23 03:25:27 +00:00
|
|
|
tpl.context.viewns = setmetatable({
|
2015-01-15 15:32:03 +00:00
|
|
|
write = http.write;
|
2009-07-24 15:45:29 +00:00
|
|
|
include = function(name) tpl.Template(name):render(getfenv(2)) end;
|
2011-08-12 11:13:39 +00:00
|
|
|
translate = i18n.translate;
|
2012-08-14 15:31:26 +00:00
|
|
|
translatef = i18n.translatef;
|
2010-10-15 16:12:07 +00:00
|
|
|
export = function(k, v) if tpl.context.viewns[k] == nil then tpl.context.viewns[k] = v end end;
|
2009-07-24 15:45:29 +00:00
|
|
|
striptags = util.striptags;
|
2009-10-31 15:42:07 +00:00
|
|
|
pcdata = util.pcdata;
|
2009-07-24 15:45:29 +00:00
|
|
|
media = media;
|
|
|
|
theme = fs.basename(media);
|
2011-10-26 02:17:45 +00:00
|
|
|
resource = luci.config.main.resourcebase;
|
2011-10-30 15:00:54 +00:00
|
|
|
ifattr = function(...) return _ifattr(...) end;
|
|
|
|
attr = function(...) return _ifattr(true, ...) end;
|
2015-10-07 11:03:42 +00:00
|
|
|
url = build_url;
|
2018-04-04 21:24:31 +00:00
|
|
|
}, {__index=function(tbl, key)
|
2008-12-14 21:43:10 +00:00
|
|
|
if key == "controller" then
|
2008-12-15 10:40:45 +00:00
|
|
|
return build_url()
|
2008-12-14 21:43:10 +00:00
|
|
|
elseif key == "REQUEST_URI" then
|
2009-01-04 15:45:57 +00:00
|
|
|
return build_url(unpack(ctx.requestpath))
|
2018-04-04 21:24:31 +00:00
|
|
|
elseif key == "FULL_REQUEST_URI" then
|
2018-05-31 15:41:40 +00:00
|
|
|
local url = { http.getenv("SCRIPT_NAME") or "", http.getenv("PATH_INFO") }
|
2018-04-04 21:24:31 +00:00
|
|
|
local query = http.getenv("QUERY_STRING")
|
|
|
|
if query and #query > 0 then
|
|
|
|
url[#url+1] = "?"
|
|
|
|
url[#url+1] = query
|
|
|
|
end
|
|
|
|
return table.concat(url, "")
|
2015-10-20 22:31:27 +00:00
|
|
|
elseif key == "token" then
|
|
|
|
return ctx.authtoken
|
2008-12-14 21:43:10 +00:00
|
|
|
else
|
2018-04-04 21:24:31 +00:00
|
|
|
return rawget(tbl, key) or _G[key]
|
2008-12-14 21:43:10 +00:00
|
|
|
end
|
|
|
|
end})
|
2008-07-17 16:02:29 +00:00
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-09-05 14:34:59 +00:00
|
|
|
track.dependent = (track.dependent ~= false)
|
2011-07-20 23:57:32 +00:00
|
|
|
assert(not track.dependent or not track.auto,
|
|
|
|
"Access Violation\nThe page at '" .. table.concat(request, "/") .. "/' " ..
|
|
|
|
"has no parent node so the access to this location has been denied.\n" ..
|
|
|
|
"This is a software bug, please report this message at " ..
|
2016-02-02 10:07:16 +00:00
|
|
|
"https://github.com/openwrt/luci/issues"
|
2011-07-20 23:57:32 +00:00
|
|
|
)
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2018-01-17 17:49:08 +00:00
|
|
|
if track.sysauth and not ctx.authsession then
|
2017-07-09 19:26:49 +00:00
|
|
|
local authen = track.sysauth_authenticator
|
|
|
|
local _, sid, sdat, default_user, allowed_users
|
2008-12-14 21:43:10 +00:00
|
|
|
|
2017-07-11 12:12:50 +00:00
|
|
|
if type(authen) == "string" and authen ~= "htmlauth" then
|
|
|
|
error500("Unsupported authenticator %q configured" % authen)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2017-07-09 19:26:49 +00:00
|
|
|
if type(track.sysauth) == "table" then
|
|
|
|
default_user, allowed_users = nil, track.sysauth
|
2009-07-25 10:47:26 +00:00
|
|
|
else
|
2017-07-09 19:26:49 +00:00
|
|
|
default_user, allowed_users = track.sysauth, { track.sysauth }
|
2008-12-14 21:43:10 +00:00
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2017-07-09 19:26:49 +00:00
|
|
|
if type(authen) == "function" then
|
|
|
|
_, sid = authen(sys.user.checkpasswd, allowed_users)
|
2017-07-11 12:12:50 +00:00
|
|
|
else
|
|
|
|
sid = http.getcookie("sysauth")
|
|
|
|
end
|
2015-01-15 09:55:53 +00:00
|
|
|
|
2017-07-11 12:12:50 +00:00
|
|
|
sid, sdat = session_retrieve(sid, allowed_users)
|
2015-02-09 15:30:11 +00:00
|
|
|
|
2017-07-11 12:12:50 +00:00
|
|
|
if not (sid and sdat) and authen == "htmlauth" then
|
|
|
|
local user = http.getenv("HTTP_AUTH_USER")
|
|
|
|
local pass = http.getenv("HTTP_AUTH_PASS")
|
2015-02-09 15:30:11 +00:00
|
|
|
|
2017-07-11 12:12:50 +00:00
|
|
|
if user == nil and pass == nil then
|
|
|
|
user = http.formvalue("luci_username")
|
|
|
|
pass = http.formvalue("luci_password")
|
|
|
|
end
|
2017-07-09 19:26:49 +00:00
|
|
|
|
2017-07-11 12:12:50 +00:00
|
|
|
sid, sdat = session_setup(user, pass, allowed_users)
|
|
|
|
|
|
|
|
if not sid then
|
|
|
|
local tmpl = require "luci.template"
|
2017-07-09 19:26:49 +00:00
|
|
|
|
2017-07-11 12:12:50 +00:00
|
|
|
context.path = {}
|
|
|
|
|
|
|
|
http.status(403, "Forbidden")
|
2018-11-16 18:33:39 +00:00
|
|
|
http.header("X-LuCI-Login-Required", "yes")
|
2017-07-11 12:12:50 +00:00
|
|
|
tmpl.render(track.sysauth_template or "sysauth", {
|
|
|
|
duser = default_user,
|
|
|
|
fuser = user
|
|
|
|
})
|
|
|
|
|
|
|
|
return
|
2008-06-28 16:03:54 +00:00
|
|
|
end
|
2017-07-09 19:26:49 +00:00
|
|
|
|
2018-05-13 09:55:01 +00:00
|
|
|
http.header("Set-Cookie", 'sysauth=%s; path=%s; HttpOnly%s' %{
|
|
|
|
sid, build_url(), http.getenv("HTTPS") == "on" and "; secure" or ""
|
|
|
|
})
|
2017-07-11 12:12:50 +00:00
|
|
|
http.redirect(build_url(unpack(ctx.requestpath)))
|
2008-06-28 16:03:54 +00:00
|
|
|
end
|
2017-07-09 19:26:49 +00:00
|
|
|
|
|
|
|
if not sid or not sdat then
|
|
|
|
http.status(403, "Forbidden")
|
2018-11-16 18:33:39 +00:00
|
|
|
http.header("X-LuCI-Login-Required", "yes")
|
2017-07-09 19:26:49 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
ctx.authsession = sid
|
|
|
|
ctx.authtoken = sdat.token
|
|
|
|
ctx.authuser = sdat.username
|
2008-06-28 16:03:54 +00:00
|
|
|
end
|
|
|
|
|
2018-04-24 18:32:47 +00:00
|
|
|
if track.cors and http.getenv("REQUEST_METHOD") == "OPTIONS" then
|
|
|
|
luci.http.status(200, "OK")
|
|
|
|
luci.http.header("Access-Control-Allow-Origin", http.getenv("HTTP_ORIGIN") or "*")
|
|
|
|
luci.http.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2019-10-09 07:55:44 +00:00
|
|
|
if c and require_post_security(c.target, args) then
|
2015-10-22 06:30:29 +00:00
|
|
|
if not test_post_security(c) then
|
2015-10-06 13:53:35 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2008-06-28 16:03:54 +00:00
|
|
|
if track.setgroup then
|
2015-01-16 20:34:30 +00:00
|
|
|
sys.process.setgroup(track.setgroup)
|
2008-06-28 16:03:54 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
if track.setuser then
|
2015-01-16 20:34:30 +00:00
|
|
|
sys.process.setuser(track.setuser)
|
2008-06-28 16:03:54 +00:00
|
|
|
end
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
local target = nil
|
|
|
|
if c then
|
|
|
|
if type(c.target) == "function" then
|
|
|
|
target = c.target
|
|
|
|
elseif type(c.target) == "table" then
|
|
|
|
target = c.target.target
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if c and (c.index or type(target) == "function") then
|
2008-10-11 11:30:43 +00:00
|
|
|
ctx.dispatched = c
|
|
|
|
ctx.requested = ctx.requested or ctx.dispatched
|
|
|
|
end
|
|
|
|
|
2008-10-10 14:37:53 +00:00
|
|
|
if c and c.index then
|
|
|
|
local tpl = require "luci.template"
|
2008-10-11 11:30:43 +00:00
|
|
|
|
|
|
|
if util.copcall(tpl.render, "indexer", {}) then
|
2008-10-10 14:37:53 +00:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
if type(target) == "function" then
|
2008-08-29 23:26:01 +00:00
|
|
|
util.copcall(function()
|
2009-01-04 15:45:57 +00:00
|
|
|
local oldenv = getfenv(target)
|
2008-09-05 18:35:09 +00:00
|
|
|
local module = require(c.module)
|
|
|
|
local env = setmetatable({}, {__index=
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-09-05 18:35:09 +00:00
|
|
|
function(tbl, key)
|
2008-09-23 00:10:51 +00:00
|
|
|
return rawget(tbl, key) or module[key] or oldenv[key]
|
2008-09-05 18:35:09 +00:00
|
|
|
end})
|
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
setfenv(target, env)
|
2008-08-29 23:26:01 +00:00
|
|
|
end)
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2011-07-21 01:04:53 +00:00
|
|
|
local ok, err
|
2009-01-04 15:45:57 +00:00
|
|
|
if type(c.target) == "table" then
|
2011-07-21 01:04:53 +00:00
|
|
|
ok, err = util.copcall(target, c.target, unpack(args))
|
2009-01-04 15:45:57 +00:00
|
|
|
else
|
2011-07-21 01:04:53 +00:00
|
|
|
ok, err = util.copcall(target, unpack(args))
|
2009-01-04 15:45:57 +00:00
|
|
|
end
|
2018-05-31 15:41:40 +00:00
|
|
|
if not ok then
|
|
|
|
error500("Failed to execute " .. (type(c.target) == "function" and "function" or c.target.type or "unknown") ..
|
|
|
|
" dispatcher target for entry '/" .. table.concat(request, "/") .. "'.\n" ..
|
|
|
|
"The called action terminated with an exception:\n" .. tostring(err or "(unknown)"))
|
|
|
|
end
|
2008-03-21 19:30:53 +00:00
|
|
|
else
|
2011-07-20 23:57:32 +00:00
|
|
|
local root = node()
|
|
|
|
if not root or not root.target then
|
|
|
|
error404("No root node was registered, this usually happens if no module was installed.\n" ..
|
2011-10-21 17:22:48 +00:00
|
|
|
"Install luci-mod-admin-full and retry. " ..
|
2011-07-20 23:57:32 +00:00
|
|
|
"If the module is already installed, try removing the /tmp/luci-indexcache file.")
|
|
|
|
else
|
2011-07-21 01:04:53 +00:00
|
|
|
error404("No page is registered at '/" .. table.concat(request, "/") .. "'.\n" ..
|
2011-07-20 23:57:32 +00:00
|
|
|
"If this url belongs to an extension, make sure it is properly installed.\n" ..
|
|
|
|
"If the extension was recently installed, try removing the /tmp/luci-indexcache file.")
|
|
|
|
end
|
2008-03-02 21:52:58 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2008-05-22 14:04:03 +00:00
|
|
|
function createindex()
|
2015-01-15 15:32:03 +00:00
|
|
|
local controllers = { }
|
|
|
|
local base = "%s/controller/" % util.libpath()
|
|
|
|
local _, path
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2015-01-15 15:32:03 +00:00
|
|
|
for path in (fs.glob("%s*.lua" % base) or function() end) do
|
|
|
|
controllers[#controllers+1] = path
|
2008-05-26 09:45:12 +00:00
|
|
|
end
|
|
|
|
|
2015-01-15 15:32:03 +00:00
|
|
|
for path in (fs.glob("%s*/*.lua" % base) or function() end) do
|
|
|
|
controllers[#controllers+1] = path
|
2009-04-04 22:54:16 +00:00
|
|
|
end
|
2008-11-05 14:10:02 +00:00
|
|
|
|
2008-08-29 23:26:01 +00:00
|
|
|
if indexcache then
|
2009-07-19 00:24:58 +00:00
|
|
|
local cachedate = fs.stat(indexcache, "mtime")
|
2008-11-05 14:10:02 +00:00
|
|
|
if cachedate then
|
|
|
|
local realdate = 0
|
|
|
|
for _, obj in ipairs(controllers) do
|
2011-07-18 14:50:39 +00:00
|
|
|
local omtime = fs.stat(obj, "mtime")
|
2008-11-05 14:10:02 +00:00
|
|
|
realdate = (omtime and omtime > realdate) and omtime or realdate
|
|
|
|
end
|
2008-09-01 16:05:34 +00:00
|
|
|
|
2015-01-15 15:32:03 +00:00
|
|
|
if cachedate > realdate and sys.process.info("uid") == 0 then
|
2008-11-05 14:10:02 +00:00
|
|
|
assert(
|
|
|
|
sys.process.info("uid") == fs.stat(indexcache, "uid")
|
2009-06-21 13:42:26 +00:00
|
|
|
and fs.stat(indexcache, "modestr") == "rw-------",
|
2008-11-05 14:10:02 +00:00
|
|
|
"Fatal: Indexcache is not sane!"
|
|
|
|
)
|
2008-09-01 16:05:34 +00:00
|
|
|
|
2008-11-05 14:10:02 +00:00
|
|
|
index = loadfile(indexcache)()
|
|
|
|
return index
|
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
end
|
2008-08-29 23:26:01 +00:00
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2009-07-24 15:45:29 +00:00
|
|
|
index = {}
|
2008-06-02 15:36:13 +00:00
|
|
|
|
2015-01-15 15:32:03 +00:00
|
|
|
for _, path in ipairs(controllers) do
|
|
|
|
local modname = "luci.controller." .. path:sub(#base+1, #path-4):gsub("/", ".")
|
2010-12-12 20:16:13 +00:00
|
|
|
local mod = require(modname)
|
2011-07-21 01:04:53 +00:00
|
|
|
assert(mod ~= true,
|
|
|
|
"Invalid controller file found\n" ..
|
2015-01-15 15:32:03 +00:00
|
|
|
"The file '" .. path .. "' contains an invalid module line.\n" ..
|
2011-07-21 01:04:53 +00:00
|
|
|
"Please verify whether the module name is set to '" .. modname ..
|
|
|
|
"' - It must correspond to the file path!")
|
2011-10-26 00:24:17 +00:00
|
|
|
|
2008-08-29 23:26:01 +00:00
|
|
|
local idx = mod.index
|
2011-07-21 01:04:53 +00:00
|
|
|
assert(type(idx) == "function",
|
|
|
|
"Invalid controller file found\n" ..
|
2015-01-15 15:32:03 +00:00
|
|
|
"The file '" .. path .. "' contains no index() function.\n" ..
|
2011-07-21 01:04:53 +00:00
|
|
|
"Please make sure that the controller contains a valid " ..
|
|
|
|
"index function and verify the spelling!")
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2011-07-21 01:04:53 +00:00
|
|
|
index[modname] = idx
|
2008-08-29 23:26:01 +00:00
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-08-29 23:26:01 +00:00
|
|
|
if indexcache then
|
2009-06-21 13:42:26 +00:00
|
|
|
local f = nixio.open(indexcache, "w", 600)
|
|
|
|
f:writeall(util.get_bytecode(index))
|
|
|
|
f:close()
|
2008-06-02 15:36:05 +00:00
|
|
|
end
|
2008-03-21 19:30:53 +00:00
|
|
|
end
|
|
|
|
|
2008-07-29 20:32:02 +00:00
|
|
|
-- Build the index before if it does not exist yet.
|
2008-05-26 12:16:16 +00:00
|
|
|
function createtree()
|
2009-07-24 15:45:29 +00:00
|
|
|
if not index then
|
|
|
|
createindex()
|
2009-07-23 03:25:27 +00:00
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2009-07-24 15:45:29 +00:00
|
|
|
local ctx = context
|
[PATCH] Allow smarter node creation based on visibility during createtree
As I've brought up on the mailing list thread "High latency caused by full tree creation", there is a large amount of delay per LuCI request which is spent building the node tree in createtree(). Most nodes created aren't
needed for the view presented to the user and only serve to consume memory and CPU time during a page load.
My idea is to provide an easy mechanism for index()ers to determine which needs to be created and what isn't. Due to the constraints of the standard LuCI web interface, this optimization needs to establish a few rules:
* The page requested must have its node created
* All parents of the page being requested must be created, so the children inherit the track
* All the top-level nodes "Status", "System", "Services", "Network" (and others added by extensions) must be created in order to have their top-level tabs in the UI
* All peers of second-level nodes need to be created as well for the same reason, to display their links on the subindexes
To make this easy to implement in each controller, the attached patch adds an "inreq" field to each created node to indicate if it lies on the request path. To satisfy the "top level node" requirement, we always
add the top level node, then check its inreq property if the top-level node is not "in request", then the controller can exit index() early.
2011-08-12 11:05:59 +00:00
|
|
|
local tree = {nodes={}, inreq=true}
|
2009-07-24 15:45:29 +00:00
|
|
|
|
|
|
|
ctx.treecache = setmetatable({}, {__mode="v"})
|
|
|
|
ctx.tree = tree
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-09-05 19:17:48 +00:00
|
|
|
local scope = setmetatable({}, {__index = luci.dispatcher})
|
2008-05-26 12:16:16 +00:00
|
|
|
|
2009-07-24 15:45:29 +00:00
|
|
|
for k, v in pairs(index) do
|
2008-05-31 08:04:49 +00:00
|
|
|
scope._NAME = k
|
|
|
|
setfenv(v, scope)
|
2009-07-24 15:45:29 +00:00
|
|
|
v()
|
2008-05-26 12:16:16 +00:00
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2009-07-24 15:45:29 +00:00
|
|
|
return tree
|
2008-05-26 12:16:16 +00:00
|
|
|
end
|
|
|
|
|
2008-06-02 20:16:05 +00:00
|
|
|
function assign(path, clone, title, order)
|
2008-06-29 14:43:06 +00:00
|
|
|
local obj = node(unpack(path))
|
2008-06-02 20:16:05 +00:00
|
|
|
obj.nodes = nil
|
|
|
|
obj.module = nil
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-06-02 20:16:05 +00:00
|
|
|
obj.title = title
|
|
|
|
obj.order = order
|
2008-06-06 15:50:21 +00:00
|
|
|
|
2008-09-15 16:50:55 +00:00
|
|
|
setmetatable(obj, {__index = _create_node(clone)})
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-06-02 20:16:05 +00:00
|
|
|
return obj
|
|
|
|
end
|
2008-05-22 17:21:30 +00:00
|
|
|
|
2008-06-02 20:16:05 +00:00
|
|
|
function entry(path, target, title, order)
|
2008-06-29 14:42:53 +00:00
|
|
|
local c = node(unpack(path))
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-05-22 17:21:30 +00:00
|
|
|
c.target = target
|
|
|
|
c.title = title
|
|
|
|
c.order = order
|
2008-05-27 20:39:48 +00:00
|
|
|
c.module = getfenv(2)._NAME
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2008-05-22 17:21:30 +00:00
|
|
|
return c
|
|
|
|
end
|
2008-05-04 20:53:31 +00:00
|
|
|
|
2009-02-26 17:08:41 +00:00
|
|
|
-- enabling the node.
|
|
|
|
function get(...)
|
|
|
|
return _create_node({...})
|
|
|
|
end
|
|
|
|
|
2008-05-22 14:04:03 +00:00
|
|
|
function node(...)
|
2008-09-15 16:50:55 +00:00
|
|
|
local c = _create_node({...})
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2008-06-06 15:50:21 +00:00
|
|
|
c.module = getfenv(2)._NAME
|
2008-07-17 16:02:29 +00:00
|
|
|
c.auto = nil
|
2008-06-04 22:41:58 +00:00
|
|
|
|
2008-05-22 14:04:03 +00:00
|
|
|
return c
|
|
|
|
end
|
|
|
|
|
2018-04-05 19:58:41 +00:00
|
|
|
function lookup(...)
|
|
|
|
local i, path = nil, {}
|
|
|
|
for i = 1, select('#', ...) do
|
|
|
|
local name, arg = nil, tostring(select(i, ...))
|
|
|
|
for name in arg:gmatch("[^/]+") do
|
|
|
|
path[#path+1] = name
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
for i = #path, 1, -1 do
|
|
|
|
local node = context.treecache[table.concat(path, ".", 1, i)]
|
|
|
|
if node and (i == #path or node.leaf) then
|
|
|
|
return node, build_url(unpack(path))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-08-12 11:04:42 +00:00
|
|
|
function _create_node(path)
|
2008-08-29 23:26:01 +00:00
|
|
|
if #path == 0 then
|
|
|
|
return context.tree
|
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-08-29 23:26:01 +00:00
|
|
|
local name = table.concat(path, ".")
|
2011-08-12 11:04:42 +00:00
|
|
|
local c = context.treecache[name]
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-08-29 23:26:01 +00:00
|
|
|
if not c then
|
|
|
|
local last = table.remove(path)
|
2011-08-12 11:04:42 +00:00
|
|
|
local parent = _create_node(path)
|
2008-11-16 13:52:50 +00:00
|
|
|
|
2018-06-29 15:46:56 +00:00
|
|
|
c = {nodes={}, auto=true, inreq=true}
|
|
|
|
|
|
|
|
local _, n
|
|
|
|
for _, n in ipairs(path) do
|
|
|
|
if context.path[_] ~= n then
|
|
|
|
c.inreq = false
|
|
|
|
break
|
|
|
|
end
|
[PATCH] Allow smarter node creation based on visibility during createtree
As I've brought up on the mailing list thread "High latency caused by full tree creation", there is a large amount of delay per LuCI request which is spent building the node tree in createtree(). Most nodes created aren't
needed for the view presented to the user and only serve to consume memory and CPU time during a page load.
My idea is to provide an easy mechanism for index()ers to determine which needs to be created and what isn't. Due to the constraints of the standard LuCI web interface, this optimization needs to establish a few rules:
* The page requested must have its node created
* All parents of the page being requested must be created, so the children inherit the track
* All the top-level nodes "Status", "System", "Services", "Network" (and others added by extensions) must be created in order to have their top-level tabs in the UI
* All peers of second-level nodes need to be created as well for the same reason, to display their links on the subindexes
To make this easy to implement in each controller, the attached patch adds an "inreq" field to each created node to indicate if it lies on the request path. To satisfy the "top level node" requirement, we always
add the top level node, then check its inreq property if the top-level node is not "in request", then the controller can exit index() early.
2011-08-12 11:05:59 +00:00
|
|
|
end
|
2018-06-29 15:46:56 +00:00
|
|
|
|
|
|
|
c.inreq = c.inreq and (context.path[#path + 1] == last)
|
|
|
|
|
2011-08-12 11:04:42 +00:00
|
|
|
parent.nodes[last] = c
|
|
|
|
context.treecache[name] = c
|
2008-08-29 23:26:01 +00:00
|
|
|
end
|
2018-06-29 15:46:56 +00:00
|
|
|
|
2011-08-12 11:04:42 +00:00
|
|
|
return c
|
2008-08-29 23:26:01 +00:00
|
|
|
end
|
|
|
|
|
2008-05-22 14:04:03 +00:00
|
|
|
-- Subdispatchers --
|
2008-07-29 20:32:02 +00:00
|
|
|
|
2018-09-19 17:58:45 +00:00
|
|
|
function _find_eligible_node(root, prefix, deep, types, descend)
|
2018-11-27 14:23:41 +00:00
|
|
|
local children = _ordered_children(root)
|
2018-09-19 17:58:45 +00:00
|
|
|
|
|
|
|
if not root.leaf and deep ~= nil then
|
|
|
|
local sub_path = { unpack(prefix) }
|
|
|
|
|
|
|
|
if deep == false then
|
|
|
|
deep = nil
|
|
|
|
end
|
|
|
|
|
2018-11-27 14:23:41 +00:00
|
|
|
local _, child
|
|
|
|
for _, child in ipairs(children) do
|
|
|
|
sub_path[#prefix+1] = child.name
|
2018-09-19 17:58:45 +00:00
|
|
|
|
2018-11-27 14:23:41 +00:00
|
|
|
local res_path = _find_eligible_node(child.node, sub_path,
|
2018-09-19 17:58:45 +00:00
|
|
|
deep, types, true)
|
|
|
|
|
|
|
|
if res_path then
|
|
|
|
return res_path
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if descend and
|
|
|
|
(not types or
|
|
|
|
(type(root.target) == "table" and
|
|
|
|
util.contains(types, root.target.type)))
|
|
|
|
then
|
|
|
|
return prefix
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function _find_node(recurse, types)
|
|
|
|
local path = { unpack(context.path) }
|
|
|
|
local name = table.concat(path, ".")
|
|
|
|
local node = context.treecache[name]
|
|
|
|
|
|
|
|
path = _find_eligible_node(node, path, recurse, types)
|
|
|
|
|
|
|
|
if path then
|
|
|
|
dispatch(path)
|
|
|
|
else
|
|
|
|
require "luci.template".render("empty_node_placeholder")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function _firstchild()
|
|
|
|
return _find_node(false, nil)
|
2011-10-25 22:48:43 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function firstchild()
|
2018-09-19 17:58:45 +00:00
|
|
|
return { type = "firstchild", target = _firstchild }
|
|
|
|
end
|
|
|
|
|
|
|
|
function _firstnode()
|
|
|
|
return _find_node(true, { "cbi", "form", "template", "arcombine" })
|
|
|
|
end
|
|
|
|
|
|
|
|
function firstnode()
|
|
|
|
return { type = "firstnode", target = _firstnode }
|
2011-10-25 22:48:43 +00:00
|
|
|
end
|
|
|
|
|
2008-05-22 14:04:03 +00:00
|
|
|
function alias(...)
|
2008-10-30 19:09:52 +00:00
|
|
|
local req = {...}
|
|
|
|
return function(...)
|
|
|
|
for _, r in ipairs({...}) do
|
|
|
|
req[#req+1] = r
|
|
|
|
end
|
|
|
|
|
2008-06-14 14:12:12 +00:00
|
|
|
dispatch(req)
|
2008-05-04 20:53:31 +00:00
|
|
|
end
|
2008-03-21 19:30:53 +00:00
|
|
|
end
|
|
|
|
|
2008-05-29 19:18:49 +00:00
|
|
|
function rewrite(n, ...)
|
2008-10-30 19:09:52 +00:00
|
|
|
local req = {...}
|
|
|
|
return function(...)
|
|
|
|
local dispatched = util.clone(context.dispatched)
|
|
|
|
|
2008-09-23 00:10:51 +00:00
|
|
|
for i=1,n do
|
2008-10-30 19:09:52 +00:00
|
|
|
table.remove(dispatched, 1)
|
|
|
|
end
|
|
|
|
|
|
|
|
for i, r in ipairs(req) do
|
|
|
|
table.insert(dispatched, i, r)
|
2008-05-29 19:18:49 +00:00
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-10-30 19:09:52 +00:00
|
|
|
for _, r in ipairs({...}) do
|
|
|
|
dispatched[#dispatched+1] = r
|
2008-05-29 19:18:49 +00:00
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-10-30 19:09:52 +00:00
|
|
|
dispatch(dispatched)
|
2008-05-29 19:18:49 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
|
|
|
|
local function _call(self, ...)
|
2011-11-30 12:50:32 +00:00
|
|
|
local func = getfenv()[self.name]
|
|
|
|
assert(func ~= nil,
|
|
|
|
'Cannot resolve function "' .. self.name .. '". Is it misspelled or local?')
|
|
|
|
|
|
|
|
assert(type(func) == "function",
|
|
|
|
'The symbol "' .. self.name .. '" does not refer to a function but data ' ..
|
|
|
|
'of type "' .. type(func) .. '".')
|
|
|
|
|
2009-02-09 13:17:26 +00:00
|
|
|
if #self.argv > 0 then
|
2011-11-30 12:50:32 +00:00
|
|
|
return func(unpack(self.argv), ...)
|
2009-01-04 15:45:57 +00:00
|
|
|
else
|
2011-11-30 12:50:32 +00:00
|
|
|
return func(...)
|
2009-01-04 15:45:57 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2008-06-14 14:12:12 +00:00
|
|
|
function call(name, ...)
|
2009-01-04 15:45:57 +00:00
|
|
|
return {type = "call", argv = {...}, name = name, target = _call}
|
|
|
|
end
|
|
|
|
|
2015-10-20 18:58:30 +00:00
|
|
|
function post_on(params, name, ...)
|
2015-10-06 13:53:35 +00:00
|
|
|
return {
|
|
|
|
type = "call",
|
2015-10-20 18:58:30 +00:00
|
|
|
post = params,
|
2015-10-06 13:53:35 +00:00
|
|
|
argv = { ... },
|
|
|
|
name = name,
|
|
|
|
target = _call
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2015-10-20 18:58:30 +00:00
|
|
|
function post(...)
|
|
|
|
return post_on(true, ...)
|
|
|
|
end
|
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
|
|
|
|
local _template = function(self, ...)
|
|
|
|
require "luci.template".render(self.view)
|
2008-05-27 20:39:48 +00:00
|
|
|
end
|
|
|
|
|
2008-05-22 14:04:03 +00:00
|
|
|
function template(name)
|
2009-01-04 15:45:57 +00:00
|
|
|
return {type = "template", view = name, target = _template}
|
2008-05-22 14:04:03 +00:00
|
|
|
end
|
2008-05-04 20:53:31 +00:00
|
|
|
|
2008-08-29 20:36:45 +00:00
|
|
|
|
2019-04-01 13:38:00 +00:00
|
|
|
local _view = function(self, ...)
|
|
|
|
require "luci.template".render("view", { view = self.view })
|
|
|
|
end
|
|
|
|
|
|
|
|
function view(name)
|
|
|
|
return {type = "view", view = name, target = _view}
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
local function _cbi(self, ...)
|
|
|
|
local cbi = require "luci.cbi"
|
|
|
|
local tpl = require "luci.template"
|
|
|
|
local http = require "luci.http"
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
local config = self.config or {}
|
|
|
|
local maps = cbi.load(self.model, ...)
|
2008-10-20 22:35:11 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
local state = nil
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2018-04-06 10:07:01 +00:00
|
|
|
local i, res
|
2009-01-04 15:45:57 +00:00
|
|
|
for i, res in ipairs(maps) do
|
2018-04-06 10:07:01 +00:00
|
|
|
if util.instanceof(res, cbi.SimpleForm) then
|
|
|
|
io.stderr:write("Model %s returns SimpleForm but is dispatched via cbi(),\n"
|
|
|
|
% self.model)
|
|
|
|
|
|
|
|
io.stderr:write("please change %s to use the form() action instead.\n"
|
|
|
|
% table.concat(context.request, "/"))
|
|
|
|
end
|
|
|
|
|
2009-04-03 18:08:25 +00:00
|
|
|
res.flow = config
|
2009-01-04 15:45:57 +00:00
|
|
|
local cstate = res:parse()
|
2009-03-07 16:28:27 +00:00
|
|
|
if cstate and (not state or cstate < state) then
|
2009-01-04 15:45:57 +00:00
|
|
|
state = cstate
|
2008-11-05 21:12:19 +00:00
|
|
|
end
|
2009-01-04 15:45:57 +00:00
|
|
|
end
|
2008-11-05 21:12:19 +00:00
|
|
|
|
2009-06-20 07:14:36 +00:00
|
|
|
local function _resolve_path(path)
|
|
|
|
return type(path) == "table" and build_url(unpack(path)) or path
|
|
|
|
end
|
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
if config.on_valid_to and state and state > 0 and state < 2 then
|
2009-06-20 07:14:36 +00:00
|
|
|
http.redirect(_resolve_path(config.on_valid_to))
|
2009-01-04 15:45:57 +00:00
|
|
|
return
|
|
|
|
end
|
2008-11-02 13:26:41 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
if config.on_changed_to and state and state > 1 then
|
2009-06-20 07:14:36 +00:00
|
|
|
http.redirect(_resolve_path(config.on_changed_to))
|
2009-01-04 15:45:57 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
if config.on_success_to and state and state > 0 then
|
2009-06-20 07:14:36 +00:00
|
|
|
http.redirect(_resolve_path(config.on_success_to))
|
2009-01-04 15:45:57 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
if config.state_handler then
|
|
|
|
if not config.state_handler(state, maps) then
|
|
|
|
return
|
2008-11-01 18:32:02 +00:00
|
|
|
end
|
2009-01-04 15:45:57 +00:00
|
|
|
end
|
2008-11-01 18:32:02 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
http.header("X-CBI-State", state or 0)
|
2010-11-22 00:32:54 +00:00
|
|
|
|
2009-03-27 00:10:17 +00:00
|
|
|
if not config.noheader then
|
|
|
|
tpl.render("cbi/header", {state = state})
|
|
|
|
end
|
2010-11-22 00:32:54 +00:00
|
|
|
|
|
|
|
local redirect
|
2010-11-27 18:17:15 +00:00
|
|
|
local messages
|
2010-11-22 00:32:54 +00:00
|
|
|
local applymap = false
|
|
|
|
local pageaction = true
|
|
|
|
local parsechain = { }
|
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
for i, res in ipairs(maps) do
|
2010-11-22 00:32:54 +00:00
|
|
|
if res.apply_needed and res.parsechain then
|
|
|
|
local c
|
|
|
|
for _, c in ipairs(res.parsechain) do
|
|
|
|
parsechain[#parsechain+1] = c
|
|
|
|
end
|
|
|
|
applymap = true
|
2008-07-15 13:17:28 +00:00
|
|
|
end
|
2010-11-22 00:32:54 +00:00
|
|
|
|
2010-11-21 00:14:03 +00:00
|
|
|
if res.redirect then
|
|
|
|
redirect = redirect or res.redirect
|
|
|
|
end
|
2010-11-22 00:32:54 +00:00
|
|
|
|
|
|
|
if res.pageaction == false then
|
|
|
|
pageaction = false
|
|
|
|
end
|
2010-11-27 18:17:15 +00:00
|
|
|
|
|
|
|
if res.message then
|
|
|
|
messages = messages or { }
|
|
|
|
messages[#messages+1] = res.message
|
|
|
|
end
|
2008-03-02 21:52:58 +00:00
|
|
|
end
|
2010-11-22 00:32:54 +00:00
|
|
|
|
|
|
|
for i, res in ipairs(maps) do
|
|
|
|
res:render({
|
|
|
|
firstmap = (i == 1),
|
|
|
|
redirect = redirect,
|
2010-11-27 18:17:15 +00:00
|
|
|
messages = messages,
|
2010-11-22 00:32:54 +00:00
|
|
|
pageaction = pageaction,
|
|
|
|
parsechain = parsechain
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
2009-03-27 00:10:17 +00:00
|
|
|
if not config.nofooter then
|
2010-11-21 00:14:03 +00:00
|
|
|
tpl.render("cbi/footer", {
|
2018-07-26 20:12:45 +00:00
|
|
|
flow = config,
|
|
|
|
pageaction = pageaction,
|
|
|
|
redirect = redirect,
|
|
|
|
state = state,
|
|
|
|
autoapply = config.autoapply,
|
|
|
|
trigger_apply = applymap
|
2010-11-21 00:14:03 +00:00
|
|
|
})
|
2009-03-27 00:10:17 +00:00
|
|
|
end
|
2008-05-24 22:58:45 +00:00
|
|
|
end
|
2008-08-09 14:14:04 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
function cbi(model, config)
|
2015-10-20 18:58:30 +00:00
|
|
|
return {
|
|
|
|
type = "cbi",
|
2018-04-04 22:15:22 +00:00
|
|
|
post = { ["cbi.submit"] = true },
|
2015-10-20 18:58:30 +00:00
|
|
|
config = config,
|
|
|
|
model = model,
|
|
|
|
target = _cbi
|
|
|
|
}
|
2009-01-04 15:45:57 +00:00
|
|
|
end
|
2008-08-29 20:36:45 +00:00
|
|
|
|
2008-08-09 14:14:04 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
local function _arcombine(self, ...)
|
|
|
|
local argv = {...}
|
|
|
|
local target = #argv > 0 and self.targets[2] or self.targets[1]
|
|
|
|
setfenv(target.target, self.env)
|
|
|
|
target:target(unpack(argv))
|
|
|
|
end
|
2008-10-20 22:35:11 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
function arcombine(trg1, trg2)
|
|
|
|
return {type = "arcombine", env = getfenv(), target = _arcombine, targets = {trg1, trg2}}
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local function _form(self, ...)
|
|
|
|
local cbi = require "luci.cbi"
|
|
|
|
local tpl = require "luci.template"
|
|
|
|
local http = require "luci.http"
|
2008-08-09 14:14:04 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
local maps = luci.cbi.load(self.model, ...)
|
|
|
|
local state = nil
|
|
|
|
|
2018-04-06 10:07:01 +00:00
|
|
|
local i, res
|
2009-01-04 15:45:57 +00:00
|
|
|
for i, res in ipairs(maps) do
|
|
|
|
local cstate = res:parse()
|
2009-03-07 16:28:27 +00:00
|
|
|
if cstate and (not state or cstate < state) then
|
2009-01-04 15:45:57 +00:00
|
|
|
state = cstate
|
2008-08-09 14:14:04 +00:00
|
|
|
end
|
|
|
|
end
|
2009-01-04 15:45:57 +00:00
|
|
|
|
|
|
|
http.header("X-CBI-State", state or 0)
|
|
|
|
tpl.render("header")
|
|
|
|
for i, res in ipairs(maps) do
|
|
|
|
res:render()
|
|
|
|
end
|
|
|
|
tpl.render("footer")
|
|
|
|
end
|
|
|
|
|
|
|
|
function form(model)
|
2015-10-20 18:58:30 +00:00
|
|
|
return {
|
|
|
|
type = "cbi",
|
2018-04-04 22:15:22 +00:00
|
|
|
post = { ["cbi.submit"] = true },
|
2015-10-20 18:58:30 +00:00
|
|
|
model = model,
|
|
|
|
target = _form
|
|
|
|
}
|
2008-08-09 14:14:04 +00:00
|
|
|
end
|
2011-08-12 11:13:39 +00:00
|
|
|
|
|
|
|
translate = i18n.translate
|
2011-08-12 13:11:29 +00:00
|
|
|
|
|
|
|
-- This function does not actually translate the given argument but
|
|
|
|
-- is used by build/i18n-scan.pl to find translatable entries.
|
|
|
|
function _(text)
|
|
|
|
return text
|
|
|
|
end
|