5 The request dispatcher and module dispatcher generators
11 Copyright 2008 Steven Barth <steven@midlink.org>
13 Licensed under the Apache License, Version 2.0 (the "License");
14 you may not use this file except in compliance with the License.
15 You may obtain a copy of the License at
17 http://www.apache.org/licenses/LICENSE-2.0
19 Unless required by applicable law or agreed to in writing, software
20 distributed under the License is distributed on an "AS IS" BASIS,
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 See the License for the specific language governing permissions and
23 limitations under the License.
26 module("luci.dispatcher", package.seeall)
33 if (os.time() < 1000000000) then
34 os.execute('date -s '..os.date('%m%d%H%M%Y', luci.fs.mtime(luci.sys.libpath() .. "/dispatcher.lua"))..' > /dev/null 2>&1')
37 -- Local dispatch database
38 local tree = {nodes={}}
43 -- Global request object
46 -- Active dispatched node
58 function build_url(...)
59 return luci.http.dispatcher() .. "/" .. table.concat(arg, "/")
62 -- Prints an error message or renders the "error401" template if available
63 function error401(message)
64 message = message or "Unauthorized"
66 require("luci.template")
67 if not pcall(luci.template.render, "error401") then
68 luci.http.prepare_content("text/plain")
74 -- Sends a 404 error code and renders the "error404" template if available
75 function error404(message)
76 luci.http.status(404, "Not Found")
77 message = message or "Not Found"
79 require("luci.template")
80 if not pcall(luci.template.render, "error404") then
81 luci.http.prepare_content("text/plain")
87 -- Sends a 500 error code and renders the "error500" template if available
88 function error500(message)
89 luci.http.status(500, "Internal Server Error")
91 require("luci.template")
92 if not pcall(luci.template.render, "error500", {message=message}) then
93 luci.http.prepare_content("text/plain")
99 -- Creates a request object for dispatching
100 function httpdispatch()
101 local pathinfo = luci.http.env.PATH_INFO or ""
103 for node in pathinfo:gmatch("[^/]+") do
104 table.insert(request, node)
110 -- Dispatches a request
112 if not built_tree then
119 for i, s in ipairs(request) do
121 if not c or c.leaf then
125 for k, v in pairs(c) do
130 if track.sysauth then
131 local accs = track.sysauth
132 accs = (type(accs) == "string") and {accs} or accs
134 local function sysauth(user, password)
135 return (luci.util.contains(accs, user)
136 and luci.sys.user.checkpasswd(user, password))
139 if not luci.http.basic_auth(sysauth) then
146 require("luci.i18n").loadc(track.i18n)
149 if track.setgroup then
150 luci.sys.process.setgroup(track.setgroup)
153 if track.setuser then
154 luci.sys.process.setuser(track.setuser)
157 -- Init template engine
158 local tpl = require("luci.template")
159 tpl.viewns.translate = function(...) return require("luci.i18n").translate(...) end
160 tpl.viewns.controller = luci.http.dispatcher()
161 tpl.viewns.uploadctrl = luci.http.dispatcher_upload()
162 tpl.viewns.media = luci.config.main.mediaurlbase
163 tpl.viewns.resource = luci.config.main.resourcebase
164 tpl.viewns.uci = require("luci.model.uci").config
165 tpl.viewns.REQUEST_URI = luci.http.env.SCRIPT_NAME .. (luci.http.env.PATH_INFO or "")
168 if c and type(c.target) == "function" then
170 stat, mod = pcall(require, c.module)
172 luci.util.updfenv(c.target, mod)
175 stat, err = pcall(c.target)
184 -- Generates the dispatching tree
185 function createindex()
187 local path = luci.sys.libpath() .. "/controller/"
190 if pcall(require, "luci.fastindex") then
191 createindex_fastindex(path, suff)
193 createindex_plain(path, suff)
199 -- Uses fastindex to create the dispatching tree
200 function createindex_fastindex(path, suffix)
202 fi = luci.fastindex.new("index")
203 fi.add(path .. "*" .. suffix)
204 fi.add(path .. "*/*" .. suffix)
208 for k, v in pairs(fi.indexes) do
213 -- Calls the index function of all available controllers
214 -- Fallback for transition purposes / Leave it in as long as it works otherwise throw it away
215 function createindex_plain(path, suffix)
222 local controllers = luci.util.combine(
223 luci.fs.glob(path .. "*" .. suffix) or {},
224 luci.fs.glob(path .. "*/*" .. suffix) or {}
228 cache = luci.fs.mtime(indexcache)
231 luci.fs.mkdir(indexcache)
232 luci.fs.chmod(indexcache, "a=,u=rwx")
233 cache = luci.fs.mtime(indexcache)
237 for i,c in ipairs(controllers) do
238 local module = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".")
244 cachefile = indexcache .. "/" .. module
245 stime = luci.fs.mtime(c) or 0
246 ctime = luci.fs.mtime(cachefile) or 0
249 if not cache or stime > ctime then
250 stat, mod = pcall(require, module)
252 if stat and mod and type(mod.index) == "function" then
253 index[module] = mod.index
256 luci.fs.writefile(cachefile, luci.util.dump(mod.index))
260 index[module] = loadfile(cachefile)
265 -- Creates the dispatching tree from the index
266 function createtree()
267 if not built_index then
273 -- Load default translation
274 luci.i18n.loadc("default")
276 local scope = luci.util.clone(_G)
277 for k,v in pairs(_M) do
278 if type(v) == "function" then
283 for k, v in pairs(index) do
287 local stat, err = pcall(v)
297 -- Reassigns a node to another position
298 function assign(path, clone, title, order)
299 local obj = node(path)
307 for k, v in ipairs(clone) do
308 if not c.nodes[v] then
309 c.nodes[v] = {nodes={}}
315 setmetatable(obj, {__index = c})
320 -- Shortcut for creating a dispatching node
321 function entry(path, target, title, order)
327 c.module = getfenv(2)._NAME
332 -- Fetch a dispatching node
338 if type(arg[1]) == "table" then
343 for k,v in ipairs(arg) do
344 if not c.nodes[v] then
345 c.nodes[v] = {nodes={}}
351 c.module = getfenv(2)._NAME
366 function rewrite(n, ...)
370 table.remove(request, 1)
373 for i,r in ipairs(req) do
374 table.insert(request, i, r)
382 return function() getfenv()[name]() end
385 function template(name)
386 require("luci.template")
387 return function() luci.template.render(name) end
392 require("luci.template")
395 local stat, res = pcall(luci.cbi.load, model)
401 local stat, err = pcall(res.parse, res)
407 luci.template.render("cbi/header")
409 luci.template.render("cbi/footer")