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)
32 context = luci.util.threadlocal()
42 function build_url(...)
43 return luci.http.getenv("SCRIPT_NAME") .. "/" .. table.concat(arg, "/")
46 -- Sends a 404 error code and renders the "error404" template if available
47 function error404(message)
48 luci.http.status(404, "Not Found")
49 message = message or "Not Found"
51 require("luci.template")
52 if not luci.util.copcall(luci.template.render, "error404") then
53 luci.http.prepare_content("text/plain")
54 luci.http.write(message)
59 -- Sends a 500 error code and renders the "error500" template if available
60 function error500(message)
61 luci.http.status(500, "Internal Server Error")
63 require("luci.template")
64 if not luci.util.copcall(luci.template.render, "error500", {message=message}) then
65 luci.http.prepare_content("text/plain")
66 luci.http.write(message)
71 -- Renders an authorization form
72 function sysauth(default)
73 local user = luci.http.formvalue("username")
74 local pass = luci.http.formvalue("password")
76 if user and luci.sys.user.checkpasswd(user, pass) then
77 local sid = luci.sys.uniqueid(16)
78 luci.http.header("Set-Cookie", "sysauth=" .. sid.."; path=/")
79 luci.sauth.write(sid, user)
83 require("luci.template")
85 luci.template.render("sysauth", {duser=default, fuser=user})
90 -- Creates a request object for dispatching
91 function httpdispatch(request)
92 luci.http.context.request = request
94 local pathinfo = request:getenv("PATH_INFO") or ""
96 for node in pathinfo:gmatch("[^/]+") do
97 table.insert(context.request, node)
100 dispatch(context.request)
104 -- Dispatches a request
105 function dispatch(request)
106 context.path = request
109 luci.i18n.setlanguage(require("luci.config").main.lang)
111 if not context.tree then
115 local c = context.tree
118 for i, s in ipairs(request) do
120 if not c or c.leaf then
124 for k, v in pairs(c) do
130 require("luci.i18n").loadc(track.i18n)
133 -- Init template engine
134 local tpl = require("luci.template")
136 tpl.context.viewns = viewns
137 viewns.write = luci.http.write
138 viewns.translate = function(...) return require("luci.i18n").translate(...) end
139 viewns.controller = luci.http.getenv("SCRIPT_NAME")
140 viewns.media = luci.config.main.mediaurlbase
141 viewns.resource = luci.config.main.resourcebase
142 viewns.REQUEST_URI = luci.http.getenv("SCRIPT_NAME") .. (luci.http.getenv("PATH_INFO") or "")
144 if track.dependent then
145 local stat, err = pcall(assert, not track.auto)
152 if track.sysauth then
153 require("luci.sauth")
154 local def = (type(track.sysauth) == "string") and track.sysauth
155 local accs = def and {track.sysauth} or track.sysauth
156 local user = luci.sauth.read(luci.http.getcookie("sysauth"))
159 if not luci.util.contains(accs, user) then
160 if not sysauth(def) then
166 if track.setgroup then
167 luci.sys.process.setgroup(track.setgroup)
170 if track.setuser then
171 luci.sys.process.setuser(track.setuser)
174 if c and type(c.target) == "function" then
175 context.dispatched = c
176 stat, mod = luci.util.copcall(require, c.module)
178 luci.util.updfenv(c.target, mod)
181 stat, err = luci.util.copcall(c.target)
190 -- Generates the dispatching tree
191 function createindex()
192 local path = luci.sys.libpath() .. "/controller/"
195 if luci.util.copcall(require, "luci.fastindex") then
196 createindex_fastindex(path, suff)
198 createindex_plain(path, suff)
202 -- Uses fastindex to create the dispatching tree
203 function createindex_fastindex(path, suffix)
207 fi = luci.fastindex.new("index")
208 fi.add(path .. "*" .. suffix)
209 fi.add(path .. "*/*" .. suffix)
213 for k, v in pairs(fi.indexes) do
218 -- Calls the index function of all available controllers
219 -- Fallback for transition purposes / Leave it in as long as it works otherwise throw it away
220 function createindex_plain(path, suffix)
225 local controllers = luci.util.combine(
226 luci.fs.glob(path .. "*" .. suffix) or {},
227 luci.fs.glob(path .. "*/*" .. suffix) or {}
231 cache = luci.fs.mtime(indexcache)
234 luci.fs.mkdir(indexcache)
235 luci.fs.chmod(indexcache, "a=,u=rwx")
236 cache = luci.fs.mtime(indexcache)
240 for i,c in ipairs(controllers) do
241 local module = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".")
247 cachefile = indexcache .. "/" .. module
248 stime = luci.fs.mtime(c) or 0
249 ctime = luci.fs.mtime(cachefile) or 0
252 if not cache or stime > ctime then
253 stat, mod = luci.util.copcall(require, module)
255 if stat and mod and type(mod.index) == "function" then
256 index[module] = mod.index
259 luci.fs.writefile(cachefile, luci.util.get_bytecode(mod.index))
263 index[module] = loadfile(cachefile)
268 -- Creates the dispatching tree from the index
269 function createtree()
274 context.tree = {nodes={}}
277 -- Load default translation
278 luci.i18n.loadc("default")
280 local scope = luci.util.clone(_G)
281 for k,v in pairs(luci.dispatcher) do
282 if type(v) == "function" then
287 for k, v in pairs(index) do
291 local stat, err = luci.util.copcall(v)
293 error500("createtree failed: " .. k .. ": " .. err)
300 -- Reassigns a node to another position
301 function assign(path, clone, title, order)
302 local obj = node(unpack(path))
309 local c = context.tree
310 for k, v in ipairs(clone) do
311 if not c.nodes[v] then
312 c.nodes[v] = {nodes={}}
318 setmetatable(obj, {__index = c})
323 -- Shortcut for creating a dispatching node
324 function entry(path, target, title, order)
325 local c = node(unpack(path))
330 c.module = getfenv(2)._NAME
335 -- Fetch a dispatching node
337 local c = context.tree
340 for k,v in ipairs(arg) do
341 if not c.nodes[v] then
342 c.nodes[v] = {nodes={}, auto=true}
348 c.module = getfenv(2)._NAME
363 function rewrite(n, ...)
367 table.remove(context.path, 1)
370 for i,r in ipairs(req) do
371 table.insert(context.path, i, r)
378 function call(name, ...)
380 return function() return getfenv()[name](unpack(argv)) end
383 function template(name)
384 require("luci.template")
385 return function() luci.template.render(name) end
390 require("luci.template")
393 local stat, maps = luci.util.copcall(luci.cbi.load, model)
399 for i, res in ipairs(maps) do
400 local stat, err = luci.util.copcall(res.parse, res)
407 luci.template.render("cbi/header")
408 for i, res in ipairs(maps) do
411 luci.template.render("cbi/footer")