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() < luci.fs.mtime(luci.sys.libpath() .. "/dispatcher.lua")) 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 -- Local dispatch database
38 local tree = {nodes={}}
43 -- Global request object
46 -- Active dispatched node
58 function build_url(...)
59 return luci.http.dispatcher() .. "/" .. table.concat(arg, "/")
62 -- Prints an error message or renders the "error401" template if available
63 function error401(message)
64 message = message or "Unauthorized"
66 require("luci.template")
67 if not pcall(luci.template.render, "error401") then
68 luci.http.prepare_content("text/plain")
74 -- Sends a 404 error code and renders the "error404" template if available
75 function error404(message)
76 luci.http.status(404, "Not Found")
77 message = message or "Not Found"
79 require("luci.template")
80 if not pcall(luci.template.render, "error404") then
81 luci.http.prepare_content("text/plain")
87 -- Sends a 500 error code and renders the "error500" template if available
88 function error500(message)
89 luci.http.status(500, "Internal Server Error")
91 require("luci.template")
92 if not pcall(luci.template.render, "error500", {message=message}) then
93 luci.http.prepare_content("text/plain")
99 -- Creates a request object for dispatching
100 function httpdispatch()
101 local pathinfo = luci.http.env.PATH_INFO or ""
104 for s in pathinfo:gmatch("([%w-]+)") do
105 table.insert(request, s)
111 -- Dispatches a request
113 if not built_tree then
120 for i, s in ipairs(request) do
122 if not c or c.leaf then
126 for k, v in pairs(c) do
131 if track.sysauth then
132 local accs = track.sysauth
133 accs = (type(accs) == "string") and {accs} or accs
135 local function sysauth(user, password)
136 return (luci.util.contains(accs, user)
137 and luci.sys.user.checkpasswd(user, password))
140 if not luci.http.basic_auth(sysauth) then
147 require("luci.i18n").loadc(track.i18n)
150 if track.setgroup then
151 luci.sys.process.setgroup(track.setgroup)
154 if track.setuser then
155 luci.sys.process.setuser(track.setuser)
158 -- Init template engine
159 local tpl = require("luci.template")
160 tpl.viewns.translate = function(...) return require("luci.i18n").translate(...) end
161 tpl.viewns.controller = luci.http.dispatcher()
162 tpl.viewns.uploadctrl = luci.http.dispatcher_upload()
163 tpl.viewns.media = luci.config.main.mediaurlbase
164 tpl.viewns.resource = luci.config.main.resourcebase
165 tpl.viewns.uci = require("luci.model.uci").config
166 tpl.viewns.REQUEST_URI = luci.http.env.SCRIPT_NAME .. luci.http.env.PATH_INFO
169 if c and type(c.target) == "function" then
171 stat, mod = pcall(require, c.module)
173 luci.util.updfenv(c.target, mod)
176 stat, err = pcall(c.target)
185 -- Generates the dispatching tree
186 function createindex()
188 local path = luci.sys.libpath() .. "/controller/"
191 if pcall(require, "luci.fastindex") then
192 createindex_fastindex(path, suff)
194 createindex_plain(path, suff)
200 -- Uses fastindex to create the dispatching tree
201 function createindex_fastindex(path, suffix)
203 fi = luci.fastindex.new("index")
204 fi.add(path .. "*" .. suffix)
205 fi.add(path .. "*/*" .. suffix)
209 for k, v in pairs(fi.indexes) do
214 -- Calls the index function of all available controllers
215 -- Fallback for transition purposes / Leave it in as long as it works otherwise throw it away
216 function createindex_plain(path, suffix)
223 local controllers = luci.util.combine(
224 luci.fs.glob(path .. "*" .. suffix) or {},
225 luci.fs.glob(path .. "*/*" .. suffix) or {}
229 cache = luci.fs.mtime(indexcache)
232 luci.fs.mkdir(indexcache)
233 luci.fs.chmod(indexcache, "a=,u=rwx")
234 cache = luci.fs.mtime(indexcache)
238 for i,c in ipairs(controllers) do
239 local module = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".")
245 cachefile = indexcache .. "/" .. module
246 stime = luci.fs.mtime(c) or 0
247 ctime = luci.fs.mtime(cachefile) or 0
250 if not cache or stime > ctime then
251 stat, mod = pcall(require, module)
253 if stat and mod and type(mod.index) == "function" then
254 index[module] = mod.index
257 luci.fs.writefile(cachefile, luci.util.dump(mod.index))
261 index[module] = loadfile(cachefile)
266 -- Creates the dispatching tree from the index
267 function createtree()
268 if not built_index then
274 -- Load default translation
275 luci.i18n.loadc("default")
277 local scope = luci.util.clone(_G)
278 for k,v in pairs(_M) do
279 if type(v) == "function" then
284 for k, v in pairs(index) do
288 local stat, err = pcall(v)
298 -- Reassigns a node to another position
299 function assign(path, clone, title, order)
300 local obj = node(path)
307 setmetatable(obj, {__index = clone})
312 -- Shortcut for creating a dispatching node
313 function entry(path, target, title, order)
319 c.module = getfenv(2)._NAME
324 -- Fetch a dispatching node
328 if arg[1] and type(arg[1]) == "table" then
332 for k,v in ipairs(arg) do
333 if not c.nodes[v] then
334 c.nodes[v] = {nodes={}, module=getfenv(2)._NAME}
354 function rewrite(n, ...)
358 table.remove(request, 1)
361 for i,r in ipairs(req) do
362 table.insert(request, i, r)
370 return function() getfenv()[name]() end
373 function template(name)
374 require("luci.template")
375 return function() luci.template.render(name) end
380 require("luci.template")
383 local stat, res = pcall(luci.cbi.load, model)
389 local stat, err = pcall(res.parse, res)
395 luci.template.render("cbi/header")
397 luci.template.render("cbi/footer")