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 context = luci.util.threadlocal()
47 function build_url(...)
48 return luci.http.dispatcher() .. "/" .. table.concat(arg, "/")
51 -- Prints an error message or renders the "error401" template if available
52 function error401(message)
53 message = message or "Unauthorized"
55 require("luci.template")
56 if not luci.util.copcall(luci.template.render, "error401") then
57 luci.http.prepare_content("text/plain")
58 luci.http.write(message)
63 -- Sends a 404 error code and renders the "error404" template if available
64 function error404(message)
65 luci.http.status(404, "Not Found")
66 message = message or "Not Found"
68 require("luci.template")
69 if not luci.util.copcall(luci.template.render, "error404") then
70 luci.http.prepare_content("text/plain")
71 luci.http.write(message)
76 -- Sends a 500 error code and renders the "error500" template if available
77 function error500(message)
78 luci.http.status(500, "Internal Server Error")
80 require("luci.template")
81 if not luci.util.copcall(luci.template.render, "error500", {message=message}) then
82 luci.http.prepare_content("text/plain")
83 luci.http.write(message)
88 -- Creates a request object for dispatching
89 function httpdispatch(request)
90 luci.http.context.request = request
92 local pathinfo = request.env.PATH_INFO or ""
94 for node in pathinfo:gmatch("[^/]+") do
95 table.insert(context.request, node)
98 dispatch(context.request)
102 -- Dispatches a request
103 function dispatch(request)
104 context.path = request
107 luci.i18n.setlanguage(require("luci.config").main.lang)
109 if not context.tree then
113 local c = context.tree
116 for i, s in ipairs(request) do
118 if not c or c.leaf then
122 for k, v in pairs(c) do
127 if track.sysauth then
128 local accs = track.sysauth
129 accs = (type(accs) == "string") and {accs} or accs
132 local function sysauth(user, password)
133 return (luci.util.contains(accs, user)
134 and luci.sys.user.checkpasswd(user, password))
137 if not luci.http.basic_auth(sysauth) then
145 require("luci.i18n").loadc(track.i18n)
148 if track.setgroup then
149 luci.sys.process.setgroup(track.setgroup)
152 if track.setuser then
153 luci.sys.process.setuser(track.setuser)
156 -- Init template engine
157 local tpl = require("luci.template")
159 tpl.context.viewns = viewns
160 viewns.write = luci.http.write
161 viewns.translate = function(...) return require("luci.i18n").translate(...) end
162 viewns.controller = luci.http.getenv("SCRIPT_NAME")
163 viewns.media = luci.config.main.mediaurlbase
164 viewns.resource = luci.config.main.resourcebase
165 viewns.REQUEST_URI = luci.http.getenv("SCRIPT_NAME") .. (luci.http.getenv("PATH_INFO") or "")
168 if c and type(c.target) == "function" then
169 context.dispatched = c
170 stat, mod = luci.util.copcall(require, c.module)
172 luci.util.updfenv(c.target, mod)
175 stat, err = luci.util.copcall(c.target)
184 -- Generates the dispatching tree
185 function createindex()
186 local path = luci.sys.libpath() .. "/controller/"
189 if luci.util.copcall(require, "luci.fastindex") then
190 createindex_fastindex(path, suff)
192 createindex_plain(path, suff)
196 -- Uses fastindex to create the dispatching tree
197 function createindex_fastindex(path, suffix)
201 fi = luci.fastindex.new("index")
202 fi.add(path .. "*" .. suffix)
203 fi.add(path .. "*/*" .. suffix)
207 for k, v in pairs(fi.indexes) do
212 -- Calls the index function of all available controllers
213 -- Fallback for transition purposes / Leave it in as long as it works otherwise throw it away
214 function createindex_plain(path, suffix)
219 local controllers = luci.util.combine(
220 luci.fs.glob(path .. "*" .. suffix) or {},
221 luci.fs.glob(path .. "*/*" .. suffix) or {}
225 cache = luci.fs.mtime(indexcache)
228 luci.fs.mkdir(indexcache)
229 luci.fs.chmod(indexcache, "a=,u=rwx")
230 cache = luci.fs.mtime(indexcache)
234 for i,c in ipairs(controllers) do
235 local module = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".")
241 cachefile = indexcache .. "/" .. module
242 stime = luci.fs.mtime(c) or 0
243 ctime = luci.fs.mtime(cachefile) or 0
246 if not cache or stime > ctime then
247 stat, mod = luci.util.copcall(require, module)
249 if stat and mod and type(mod.index) == "function" then
250 index[module] = mod.index
253 luci.fs.writefile(cachefile, luci.util.dump(mod.index))
257 index[module] = loadfile(cachefile)
262 -- Creates the dispatching tree from the index
263 function createtree()
268 context.tree = {nodes={}}
271 -- Load default translation
272 luci.i18n.loadc("default")
274 local scope = luci.util.clone(_G)
275 for k,v in pairs(_M) do
276 if type(v) == "function" then
281 for k, v in pairs(index) do
285 local stat, err = luci.util.copcall(v)
293 -- Reassigns a node to another position
294 function assign(path, clone, title, order)
295 local obj = node(path)
302 local c = context.tree
303 for k, v in ipairs(clone) do
304 if not c.nodes[v] then
305 c.nodes[v] = {nodes={}}
311 setmetatable(obj, {__index = c})
316 -- Shortcut for creating a dispatching node
317 function entry(path, target, title, order)
323 c.module = getfenv(2)._NAME
328 -- Fetch a dispatching node
330 local c = context.tree
333 if type(arg[1]) == "table" then
338 for k,v in ipairs(arg) do
339 if not c.nodes[v] then
340 c.nodes[v] = {nodes={}}
346 c.module = getfenv(2)._NAME
360 function rewrite(n, ...)
364 table.remove(context.path, 1)
367 for i,r in ipairs(req) do
368 table.insert(context.path, i, r)
375 function call(name, ...)
377 return function() return getfenv()[name](unpack(argv)) end
380 function template(name)
381 require("luci.template")
382 return function() luci.template.render(name) end
387 require("luci.template")
390 local stat, res = luci.util.copcall(luci.cbi.load, model)
396 local stat, err = luci.util.copcall(res.parse, res)
402 luci.template.render("cbi/header")
404 luci.template.render("cbi/footer")