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 -- Sends a 404 error code and renders the "error404" template if available
62 function error404(message)
63 luci.http.status(404, "Not Found")
64 message = message or "Not Found"
66 require("luci.template")
67 if not pcall(luci.template.render, "error404") then
68 luci.http.prepare_content("text/plain")
74 -- Sends a 500 error code and renders the "error500" template if available
75 function error500(message)
76 luci.http.status(500, "Internal Server Error")
78 require("luci.template")
79 if not pcall(luci.template.render, "error500", {message=message}) then
80 luci.http.prepare_content("text/plain")
86 -- Creates a request object for dispatching
87 function httpdispatch()
88 local pathinfo = luci.http.env.PATH_INFO or ""
91 for s in pathinfo:gmatch("([%w-]+)") do
92 table.insert(request, s)
98 -- Dispatches a request
100 if not built_tree then
107 for i, s in ipairs(request) do
109 if not c or c.leaf then
113 for k, v in pairs(c) do
120 require("luci.i18n").loadc(track.i18n)
123 if track.setgroup then
124 luci.sys.process.setgroup(track.setgroup)
127 if track.setuser then
128 luci.sys.process.setuser(track.setuser)
131 -- Init template engine
132 local tpl = require("luci.template")
133 tpl.viewns.translate = function(...) return require("luci.i18n").translate(...) end
134 tpl.viewns.controller = luci.http.dispatcher()
135 tpl.viewns.uploadctrl = luci.http.dispatcher_upload()
136 tpl.viewns.media = luci.config.main.mediaurlbase
137 tpl.viewns.resource = luci.config.main.resourcebase
138 tpl.viewns.REQUEST_URI = luci.http.env.SCRIPT_NAME .. luci.http.env.PATH_INFO
141 if c and type(c.target) == "function" then
143 stat, mod = pcall(require, c.module)
145 luci.util.updfenv(c.target, mod)
148 stat, err = pcall(c.target)
157 -- Generates the dispatching tree
158 function createindex()
160 local path = luci.sys.libpath() .. "/controller/"
163 if pcall(require, "luci.fastindex") then
164 createindex_fastindex(path, suff)
166 createindex_plain(path, suff)
172 -- Uses fastindex to create the dispatching tree
173 function createindex_fastindex(path, suffix)
175 fi = luci.fastindex.new("index")
176 fi.add(path .. "*" .. suffix)
177 fi.add(path .. "*/*" .. suffix)
181 for k, v in pairs(fi.indexes) do
186 -- Calls the index function of all available controllers
187 -- Fallback for transition purposes / Leave it in as long as it works otherwise throw it away
188 function createindex_plain(path, suffix)
195 local controllers = luci.util.combine(
196 luci.fs.glob(path .. "*" .. suffix) or {},
197 luci.fs.glob(path .. "*/*" .. suffix) or {}
201 cache = luci.fs.mtime(indexcache)
204 luci.fs.mkdir(indexcache)
205 luci.fs.chmod(indexcache, "a=,u=rwx")
206 cache = luci.fs.mtime(indexcache)
210 for i,c in ipairs(controllers) do
211 local module = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".")
217 cachefile = indexcache .. "/" .. module
218 stime = luci.fs.mtime(c) or 0
219 ctime = luci.fs.mtime(cachefile) or 0
222 if not cache or stime > ctime then
223 stat, mod = pcall(require, module)
225 if stat and mod and type(mod.index) == "function" then
226 index[module] = mod.index
229 luci.fs.writefile(cachefile, luci.util.dump(mod.index))
233 index[module] = loadfile(cachefile)
238 -- Creates the dispatching tree from the index
239 function createtree()
240 if not built_index then
246 -- Load default translation
247 luci.i18n.loadc("default")
249 local scope = luci.util.clone(_G)
250 for k,v in pairs(_M) do
251 if type(v) == "function" then
256 for k, v in pairs(index) do
260 local stat, err = pcall(v)
270 -- Shortcut for creating a dispatching node
271 function entry(path, target, title, order, add)
278 c.module = getfenv(2)._NAME
280 for k,v in pairs(add) do
287 -- Fetch a dispatching node
291 if arg[1] and type(arg[1]) == "table" then
295 for k,v in ipairs(arg) do
296 if not c.nodes[v] then
297 c.nodes[v] = {nodes={}, module=getfenv(2)._NAME}
315 function rewrite(n, ...)
319 table.remove(request, 1)
322 for i,r in ipairs(req) do
323 table.insert(request, i, r)
331 return function() getfenv()[name]() end
334 function template(name)
335 require("luci.template")
336 return function() luci.template.render(name) end
341 require("luci.template")
344 local stat, res = pcall(luci.cbi.load, model)
350 local stat, err = pcall(res.parse, res)
356 luci.template.render("cbi/header")
358 luci.template.render("cbi/footer")