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)
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.sysauth then
145 require("luci.sauth")
146 local def = (type(track.sysauth) == "string") and track.sysauth
147 local accs = def and {track.sysauth} or track.sysauth
148 local user = luci.sauth.read(luci.http.getcookie("sysauth"))
151 if not luci.util.contains(accs, user) then
152 if not sysauth(def) then
158 if track.setgroup then
159 luci.sys.process.setgroup(track.setgroup)
162 if track.setuser then
163 luci.sys.process.setuser(track.setuser)
166 if c and type(c.target) == "function" then
167 context.dispatched = c
168 stat, mod = luci.util.copcall(require, c.module)
170 luci.util.updfenv(c.target, mod)
173 stat, err = luci.util.copcall(c.target)
182 -- Generates the dispatching tree
183 function createindex()
184 local path = luci.sys.libpath() .. "/controller/"
187 if luci.util.copcall(require, "luci.fastindex") then
188 createindex_fastindex(path, suff)
190 createindex_plain(path, suff)
194 -- Uses fastindex to create the dispatching tree
195 function createindex_fastindex(path, suffix)
199 fi = luci.fastindex.new("index")
200 fi.add(path .. "*" .. suffix)
201 fi.add(path .. "*/*" .. suffix)
205 for k, v in pairs(fi.indexes) do
210 -- Calls the index function of all available controllers
211 -- Fallback for transition purposes / Leave it in as long as it works otherwise throw it away
212 function createindex_plain(path, suffix)
217 local controllers = luci.util.combine(
218 luci.fs.glob(path .. "*" .. suffix) or {},
219 luci.fs.glob(path .. "*/*" .. suffix) or {}
223 cache = luci.fs.mtime(indexcache)
226 luci.fs.mkdir(indexcache)
227 luci.fs.chmod(indexcache, "a=,u=rwx")
228 cache = luci.fs.mtime(indexcache)
232 for i,c in ipairs(controllers) do
233 local module = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".")
239 cachefile = indexcache .. "/" .. module
240 stime = luci.fs.mtime(c) or 0
241 ctime = luci.fs.mtime(cachefile) or 0
244 if not cache or stime > ctime then
245 stat, mod = luci.util.copcall(require, module)
247 if stat and mod and type(mod.index) == "function" then
248 index[module] = mod.index
251 luci.fs.writefile(cachefile, luci.util.dump(mod.index))
255 index[module] = loadfile(cachefile)
260 -- Creates the dispatching tree from the index
261 function createtree()
266 context.tree = {nodes={}}
269 -- Load default translation
270 luci.i18n.loadc("default")
272 local scope = luci.util.clone(_G)
273 for k,v in pairs(luci.dispatcher) do
274 if type(v) == "function" then
279 for k, v in pairs(index) do
283 local stat, err = luci.util.copcall(v)
285 error500("createtree failed: " .. k .. ": " .. err)
291 -- Reassigns a node to another position
292 function assign(path, clone, title, order)
293 local obj = node(path)
300 local c = context.tree
301 for k, v in ipairs(clone) do
302 if not c.nodes[v] then
303 c.nodes[v] = {nodes={}}
309 setmetatable(obj, {__index = c})
314 -- Shortcut for creating a dispatching node
315 function entry(path, target, title, order)
321 c.module = getfenv(2)._NAME
326 -- Fetch a dispatching node
328 local c = context.tree
331 if type(arg[1]) == "table" then
336 for k,v in ipairs(arg) do
337 if not c.nodes[v] then
338 c.nodes[v] = {nodes={}}
344 c.module = getfenv(2)._NAME
358 function rewrite(n, ...)
362 table.remove(context.path, 1)
365 for i,r in ipairs(req) do
366 table.insert(context.path, i, r)
373 function call(name, ...)
375 return function() return getfenv()[name](unpack(argv)) end
378 function template(name)
379 require("luci.template")
380 return function() luci.template.render(name) end
385 require("luci.template")
388 local stat, res = luci.util.copcall(luci.cbi.load, model)
394 local stat, err = luci.util.copcall(res.parse, res)
400 luci.template.render("cbi/header")
402 luci.template.render("cbi/footer")