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.dispatcher() .. "/" .. table.concat(arg, "/")
46 -- Prints an error message or renders the "error401" template if available
47 function error401(message)
48 message = message or "Unauthorized"
50 require("luci.template")
51 if not luci.util.copcall(luci.template.render, "error401") then
52 luci.http.prepare_content("text/plain")
53 luci.http.write(message)
58 -- Sends a 404 error code and renders the "error404" template if available
59 function error404(message)
60 luci.http.status(404, "Not Found")
61 message = message or "Not Found"
63 require("luci.template")
64 if not luci.util.copcall(luci.template.render, "error404") then
65 luci.http.prepare_content("text/plain")
66 luci.http.write(message)
71 -- Sends a 500 error code and renders the "error500" template if available
72 function error500(message)
73 luci.http.status(500, "Internal Server Error")
75 require("luci.template")
76 if not luci.util.copcall(luci.template.render, "error500", {message=message}) then
77 luci.http.prepare_content("text/plain")
78 luci.http.write(message)
83 -- Creates a request object for dispatching
84 function httpdispatch(request)
85 luci.http.context.request = request
87 local pathinfo = request.env.PATH_INFO or ""
89 for node in pathinfo:gmatch("[^/]+") do
90 table.insert(context.request, node)
93 dispatch(context.request)
97 -- Dispatches a request
98 function dispatch(request)
99 context.path = request
102 luci.i18n.setlanguage(require("luci.config").main.lang)
104 if not context.tree then
108 local c = context.tree
111 for i, s in ipairs(request) do
113 if not c or c.leaf then
117 for k, v in pairs(c) do
122 if track.sysauth then
123 local accs = track.sysauth
124 accs = (type(accs) == "string") and {accs} or accs
127 local function sysauth(user, password)
128 return (luci.util.contains(accs, user)
129 and luci.sys.user.checkpasswd(user, password))
132 if not luci.http.basic_auth(sysauth) then
140 require("luci.i18n").loadc(track.i18n)
143 if track.setgroup then
144 luci.sys.process.setgroup(track.setgroup)
147 if track.setuser then
148 luci.sys.process.setuser(track.setuser)
151 -- Init template engine
152 local tpl = require("luci.template")
154 tpl.context.viewns = viewns
155 viewns.write = luci.http.write
156 viewns.translate = function(...) return require("luci.i18n").translate(...) end
157 viewns.controller = luci.http.getenv("SCRIPT_NAME")
158 viewns.media = luci.config.main.mediaurlbase
159 viewns.resource = luci.config.main.resourcebase
160 viewns.REQUEST_URI = luci.http.getenv("SCRIPT_NAME") .. (luci.http.getenv("PATH_INFO") or "")
163 if c and type(c.target) == "function" then
164 context.dispatched = c
165 stat, mod = luci.util.copcall(require, c.module)
167 luci.util.updfenv(c.target, mod)
170 stat, err = luci.util.copcall(c.target)
179 -- Generates the dispatching tree
180 function createindex()
181 local path = luci.sys.libpath() .. "/controller/"
184 if luci.util.copcall(require, "luci.fastindex") then
185 createindex_fastindex(path, suff)
187 createindex_plain(path, suff)
191 -- Uses fastindex to create the dispatching tree
192 function createindex_fastindex(path, suffix)
196 fi = luci.fastindex.new("index")
197 fi.add(path .. "*" .. suffix)
198 fi.add(path .. "*/*" .. suffix)
202 for k, v in pairs(fi.indexes) do
207 -- Calls the index function of all available controllers
208 -- Fallback for transition purposes / Leave it in as long as it works otherwise throw it away
209 function createindex_plain(path, suffix)
214 local controllers = luci.util.combine(
215 luci.fs.glob(path .. "*" .. suffix) or {},
216 luci.fs.glob(path .. "*/*" .. suffix) or {}
220 cache = luci.fs.mtime(indexcache)
223 luci.fs.mkdir(indexcache)
224 luci.fs.chmod(indexcache, "a=,u=rwx")
225 cache = luci.fs.mtime(indexcache)
229 for i,c in ipairs(controllers) do
230 local module = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".")
236 cachefile = indexcache .. "/" .. module
237 stime = luci.fs.mtime(c) or 0
238 ctime = luci.fs.mtime(cachefile) or 0
241 if not cache or stime > ctime then
242 stat, mod = luci.util.copcall(require, module)
244 if stat and mod and type(mod.index) == "function" then
245 index[module] = mod.index
248 luci.fs.writefile(cachefile, luci.util.dump(mod.index))
252 index[module] = loadfile(cachefile)
257 -- Creates the dispatching tree from the index
258 function createtree()
263 context.tree = {nodes={}}
266 -- Load default translation
267 luci.i18n.loadc("default")
269 local scope = luci.util.clone(_G)
270 for k,v in pairs(_M) do
271 if type(v) == "function" then
276 for k, v in pairs(index) do
280 local stat, err = luci.util.copcall(v)
288 -- Reassigns a node to another position
289 function assign(path, clone, title, order)
290 local obj = node(path)
297 local c = context.tree
298 for k, v in ipairs(clone) do
299 if not c.nodes[v] then
300 c.nodes[v] = {nodes={}}
306 setmetatable(obj, {__index = c})
311 -- Shortcut for creating a dispatching node
312 function entry(path, target, title, order)
318 c.module = getfenv(2)._NAME
323 -- Fetch a dispatching node
325 local c = context.tree
328 if type(arg[1]) == "table" then
333 for k,v in ipairs(arg) do
334 if not c.nodes[v] then
335 c.nodes[v] = {nodes={}}
341 c.module = getfenv(2)._NAME
355 function rewrite(n, ...)
359 table.remove(context.path, 1)
362 for i,r in ipairs(req) do
363 table.insert(context.path, i, r)
370 function call(name, ...)
372 return function() return getfenv()[name](unpack(argv)) end
375 function template(name)
376 require("luci.template")
377 return function() luci.template.render(name) end
382 require("luci.template")
385 local stat, res = luci.util.copcall(luci.cbi.load, model)
391 local stat, err = luci.util.copcall(res.parse, res)
397 luci.template.render("cbi/header")
399 luci.template.render("cbi/footer")