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 if (os.time() < luci.fs.mtime(luci.sys.libpath() .. "/dispatcher.lua")) then
33 os.execute('date -s '..os.date('%m%d%H%M%Y', luci.fs.mtime(luci.sys.libpath() .. "/dispatcher.lua"))..' > /dev/null 2>&1')
36 -- Local dispatch database
37 local tree = {nodes={}}
42 -- Global request object
45 -- Active dispatched node
57 function build_url(...)
58 return luci.http.dispatcher() .. "/" .. table.concat(arg, "/")
61 -- Prints an error message or renders the "error401" template if available
62 function error401(message)
63 message = message or "Unauthorized"
65 require("luci.template")
66 if not pcall(luci.template.render, "error401") then
67 luci.http.prepare_content("text/plain")
73 -- Sends a 404 error code and renders the "error404" template if available
74 function error404(message)
75 luci.http.status(404, "Not Found")
76 message = message or "Not Found"
78 require("luci.template")
79 if not pcall(luci.template.render, "error404") then
80 luci.http.prepare_content("text/plain")
86 -- Sends a 500 error code and renders the "error500" template if available
87 function error500(message)
88 luci.http.status(500, "Internal Server Error")
90 require("luci.template")
91 if not pcall(luci.template.render, "error500", {message=message}) then
92 luci.http.prepare_content("text/plain")
98 -- Creates a request object for dispatching
99 function httpdispatch()
100 local pathinfo = luci.http.env.PATH_INFO or ""
103 for s in pathinfo:gmatch("([%w-]+)") do
104 table.insert(request, s)
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.REQUEST_URI = luci.http.env.SCRIPT_NAME .. luci.http.env.PATH_INFO
167 if c and type(c.target) == "function" then
169 stat, mod = pcall(require, c.module)
171 luci.util.updfenv(c.target, mod)
174 stat, err = pcall(c.target)
183 -- Generates the dispatching tree
184 function createindex()
186 local path = luci.sys.libpath() .. "/controller/"
189 if pcall(require, "luci.fastindex") then
190 createindex_fastindex(path, suff)
192 createindex_plain(path, suff)
198 -- Uses fastindex to create the dispatching tree
199 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)
221 local controllers = luci.util.combine(
222 luci.fs.glob(path .. "*" .. suffix) or {},
223 luci.fs.glob(path .. "*/*" .. suffix) or {}
227 cache = luci.fs.mtime(indexcache)
230 luci.fs.mkdir(indexcache)
231 luci.fs.chmod(indexcache, "a=,u=rwx")
232 cache = luci.fs.mtime(indexcache)
236 for i,c in ipairs(controllers) do
237 local module = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".")
243 cachefile = indexcache .. "/" .. module
244 stime = luci.fs.mtime(c) or 0
245 ctime = luci.fs.mtime(cachefile) or 0
248 if not cache or stime > ctime then
249 stat, mod = pcall(require, module)
251 if stat and mod and type(mod.index) == "function" then
252 index[module] = mod.index
255 luci.fs.writefile(cachefile, luci.util.dump(mod.index))
259 index[module] = loadfile(cachefile)
264 -- Creates the dispatching tree from the index
265 function createtree()
266 if not built_index then
272 -- Load default translation
273 luci.i18n.loadc("default")
275 local scope = luci.util.clone(_G)
276 for k,v in pairs(_M) do
277 if type(v) == "function" then
282 for k, v in pairs(index) do
286 local stat, err = pcall(v)
296 -- Reassigns a node to another position
297 function assign(path, clone, title, order)
298 local obj = node(path)
305 setmetatable(obj, {__index = clone})
310 -- Shortcut for creating a dispatching node
311 function entry(path, target, title, order)
317 c.module = getfenv(2)._NAME
322 -- Fetch a dispatching node
326 if arg[1] and type(arg[1]) == "table" then
330 for k,v in ipairs(arg) do
331 if not c.nodes[v] then
332 c.nodes[v] = {nodes={}, module=getfenv(2)._NAME}
350 function rewrite(n, ...)
354 table.remove(request, 1)
357 for i,r in ipairs(req) do
358 table.insert(request, i, r)
366 return function() getfenv()[name]() end
369 function template(name)
370 require("luci.template")
371 return function() luci.template.render(name) end
376 require("luci.template")
379 local stat, res = pcall(luci.cbi.load, model)
385 local stat, err = pcall(res.parse, res)
391 luci.template.render("cbi/header")
393 luci.template.render("cbi/footer")