* Added timing fix
[project/luci.git] / libs / web / luasrc / dispatcher.lua
1 --[[
2 LuCI - Dispatcher
3
4 Description:
5 The request dispatcher and module dispatcher generators
6
7 FileId:
8 $Id$
9
10 License:
11 Copyright 2008 Steven Barth <steven@midlink.org>
12
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
16
17 http://www.apache.org/licenses/LICENSE-2.0
18
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.
24
25 ]]--
26 module("luci.dispatcher", package.seeall)
27 require("luci.http")
28 require("luci.sys")
29 require("luci.fs")
30
31 -- Dirty OpenWRT fix
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")))
34 end
35
36 -- Local dispatch database
37 local tree = {nodes={}}
38
39 -- Index table
40 local index = {}
41
42 -- Global request object
43 request = {}
44
45 -- Active dispatched node
46 dispatched = nil
47
48 -- Status fields
49 built_index = false
50 built_tree = false
51
52
53 -- Builds a URL
54 function build_url(...)
55 return luci.http.dispatcher() .. "/" .. table.concat(arg, "/")
56 end
57
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"
62
63 require("luci.template")
64 if not pcall(luci.template.render, "error404") then
65 luci.http.prepare_content("text/plain")
66 print(message)
67 end
68 return false
69 end
70
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")
74
75 require("luci.template")
76 if not pcall(luci.template.render, "error500", {message=message}) then
77 luci.http.prepare_content("text/plain")
78 print(message)
79 end
80 return false
81 end
82
83 -- Creates a request object for dispatching
84 function httpdispatch()
85 local pathinfo = luci.http.env.PATH_INFO or ""
86 local c = tree
87
88 for s in pathinfo:gmatch("([%w-]+)") do
89 table.insert(request, s)
90 end
91
92 dispatch()
93 end
94
95 -- Dispatches a request
96 function dispatch()
97 if not built_tree then
98 createtree()
99 end
100
101 local c = tree
102 local track = {}
103
104 for i, s in ipairs(request) do
105 c = c.nodes[s]
106 if not c or c.leaf then
107 break
108 end
109
110 for k, v in pairs(c) do
111 track[k] = v
112 end
113 end
114
115
116 if track.i18n then
117 require("luci.i18n").loadc(track.i18n)
118 end
119
120 if track.setgroup then
121 luci.sys.process.setgroup(track.setgroup)
122 end
123
124 if track.setuser then
125 luci.sys.process.setuser(track.setuser)
126 end
127
128 -- Init template engine
129 local tpl = require("luci.template")
130 tpl.viewns.translate = function(...) return require("luci.i18n").translate(...) end
131 tpl.viewns.controller = luci.http.dispatcher()
132 tpl.viewns.uploadctrl = luci.http.dispatcher_upload()
133 tpl.viewns.media = luci.config.main.mediaurlbase
134 tpl.viewns.resource = luci.config.main.resourcebase
135 tpl.viewns.REQUEST_URI = luci.http.env.SCRIPT_NAME .. luci.http.env.PATH_INFO
136
137
138 if c and type(c.target) == "function" then
139 dispatched = c
140 stat, mod = pcall(require, c.module)
141 if stat then
142 luci.util.updfenv(c.target, mod)
143 end
144
145 stat, err = pcall(c.target)
146 if not stat then
147 error500(err)
148 end
149 else
150 error404()
151 end
152 end
153
154 -- Generates the dispatching tree
155 function createindex()
156 index = {}
157 local path = luci.sys.libpath() .. "/controller/"
158 local suff = ".lua"
159
160 --[[if pcall(require, "fastindex") then
161 createindex_fastindex(path, suff)
162 else
163 createindex_plain(path, suff)
164 end]]--
165
166 createindex_plain(path, suff)
167
168 built_index = true
169 end
170
171 -- Uses fastindex to create the dispatching tree
172 function createindex_fastindex(path, suffix)
173 local fi = fastindex.new("index")
174 fi.add(path .. "*" .. suffix)
175 fi.add(path .. "*/*" .. suffix)
176 fi.scan()
177
178 for k, v in pairs(fi.indexes) do
179 index[v[2]] = v[1]
180 end
181 end
182
183 -- Calls the index function of all available controllers
184 function createindex_plain(path, suffix)
185 local cache = nil
186
187 local controllers = luci.util.combine(
188 luci.fs.glob(path .. "*" .. suffix) or {},
189 luci.fs.glob(path .. "*/*" .. suffix) or {}
190 )
191
192 if indexcache then
193 cache = luci.fs.mtime(indexcache)
194
195 if not cache then
196 luci.fs.mkdir(indexcache)
197 luci.fs.chmod(indexcache, "a=,u=rwx")
198 cache = luci.fs.mtime(indexcache)
199 end
200 end
201
202 for i,c in ipairs(controllers) do
203 local module = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".")
204 local cachefile = indexcache .. "/" .. module
205 local stime
206 local ctime
207
208 if cache then
209 stime = luci.fs.mtime(c) or 0
210 ctime = luci.fs.mtime(cachefile) or 0
211 end
212
213 if not cache or stime > ctime then
214 stat, mod = pcall(require, module)
215
216 if stat and mod and type(mod.index) == "function" then
217 index[module] = mod.index
218
219 if cache then
220 luci.fs.writefile(cachefile, luci.util.dump(mod.index))
221 end
222 end
223 else
224 index[module] = loadfile(cachefile)
225 end
226 end
227 end
228
229 -- Creates the dispatching tree from the index
230 function createtree()
231 if not built_index then
232 createindex()
233 end
234
235 require("luci.i18n")
236
237 -- Load default translation
238 luci.i18n.loadc("default")
239
240 local scope = luci.util.clone(_G)
241 for k,v in pairs(_M) do
242 if type(v) == "function" then
243 scope[k] = v
244 end
245 end
246
247 for k, v in pairs(index) do
248 scope._NAME = k
249 setfenv(v, scope)
250
251 local stat, err = pcall(v)
252 if not stat then
253 error500(err)
254 os.exit(1)
255 end
256 end
257
258 built_tree = true
259 end
260
261 -- Shortcut for creating a dispatching node
262 function entry(path, target, title, order, add)
263 add = add or {}
264
265 local c = node(path)
266 c.target = target
267 c.title = title
268 c.order = order
269 c.module = getfenv(2)._NAME
270
271 for k,v in pairs(add) do
272 c[k] = v
273 end
274
275 return c
276 end
277
278 -- Fetch a dispatching node
279 function node(...)
280 local c = tree
281
282 if arg[1] and type(arg[1]) == "table" then
283 arg = arg[1]
284 end
285
286 for k,v in ipairs(arg) do
287 if not c.nodes[v] then
288 c.nodes[v] = {nodes={}, module=getfenv(2)._NAME}
289 end
290
291 c = c.nodes[v]
292 end
293
294 return c
295 end
296
297 -- Subdispatchers --
298 function alias(...)
299 local req = arg
300 return function()
301 request = req
302 dispatch()
303 end
304 end
305
306 function rewrite(n, ...)
307 local req = arg
308 return function()
309 for i=1,n do
310 table.remove(request, 1)
311 end
312
313 for i,r in ipairs(req) do
314 table.insert(request, i, r)
315 end
316
317 dispatch()
318 end
319 end
320
321 function call(name)
322 return function() getfenv()[name]() end
323 end
324
325 function template(name)
326 require("luci.template")
327 return function() luci.template.render(name) end
328 end
329
330 function cbi(model)
331 require("luci.cbi")
332 require("luci.template")
333
334 return function()
335 local stat, res = pcall(luci.cbi.load, model)
336 if not stat then
337 error500(res)
338 return true
339 end
340
341 local stat, err = pcall(res.parse, res)
342 if not stat then
343 error500(err)
344 return true
345 end
346
347 luci.template.render("cbi/header")
348 res:render()
349 luci.template.render("cbi/footer")
350 end
351 end