* Fixed caching mechanism
[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 -- Local dispatch database
32 local tree = {nodes={}}
33
34 -- Index table
35 local index = {}
36
37 -- Indexdump
38 local indexcache = "/tmp/.luciindex"
39
40 -- Global request object
41 request = {}
42
43 -- Active dispatched node
44 dispatched = nil
45
46 -- Status fields
47 built_index = false
48 built_tree  = false
49
50
51 -- Builds a URL
52 function build_url(...)
53         return luci.http.dispatcher() .. "/" .. table.concat(arg, "/")
54 end
55
56 -- Sends a 404 error code and renders the "error404" template if available
57 function error404(message)
58         luci.http.status(404, "Not Found")
59         message = message or "Not Found"
60
61         require("luci.template")
62         if not pcall(luci.template.render, "error404") then
63                 luci.http.prepare_content("text/plain")
64                 print(message)
65         end
66         return false
67 end
68
69 -- Sends a 500 error code and renders the "error500" template if available
70 function error500(message)
71         luci.http.status(500, "Internal Server Error")
72
73         require("luci.template")
74         if not pcall(luci.template.render, "error500", {message=message}) then
75                 luci.http.prepare_content("text/plain")
76                 print(message)
77         end
78         return false
79 end
80
81 -- Creates a request object for dispatching
82 function httpdispatch()
83         local pathinfo = luci.http.env.PATH_INFO or ""
84         local c = tree
85
86         for s in pathinfo:gmatch("([%w_]+)") do
87                 table.insert(request, s)
88         end
89
90         dispatch()
91 end
92
93 -- Dispatches a request
94 function dispatch()
95         if not built_tree then
96                 createtree()
97         end
98
99         local c = tree
100         local track = {}
101
102         for i, s in ipairs(request) do
103                 c = c.nodes[s]
104                 if not c then
105                         break
106                 end
107
108                 for k, v in pairs(c) do
109                         track[k] = v
110                 end
111         end
112
113
114         if track.i18n then
115                 require("luci.i18n").loadc(track.i18n)
116         end
117
118         if track.setgroup then
119                 luci.sys.process.setgroup(track.setgroup)
120         end
121
122         if track.setuser then
123                 luci.sys.process.setuser(track.setuser)
124         end
125         
126         -- Init template engine
127         local tpl = require("luci.template")
128         tpl.viewns.translate  = function(...) return require("luci.i18n").translate(...) end
129         tpl.viewns.controller = luci.http.dispatcher()
130         tpl.viewns.uploadctrl = luci.http.dispatcher_upload()
131         tpl.viewns.media      = luci.config.main.mediaurlbase
132         tpl.viewns.resource   = luci.config.main.resourcebase
133         
134         -- Load default translation
135         require("luci.i18n").loadc("default")
136         
137
138         if c and type(c.target) == "function" then
139                 dispatched = c
140
141                 stat, err = pcall(c.target)
142                 if not stat then
143                         error500(err)
144                 end
145         else
146                 error404()
147         end
148 end
149
150 -- Generates the dispatching tree
151 function createindex()
152         index = {}
153         local path = luci.sys.libpath() .. "/controller/"
154         local suff = ".lua"
155         
156         if pcall(require, "fastindex") then
157                 createindex_fastindex(path, suff)
158         else
159                 createindex_plain(path, suff)
160         end
161         
162         built_index = true
163 end
164
165 -- Uses fastindex to create the dispatching tree
166 function createindex_fastindex(path, suffix)    
167         local fi = fastindex.new("index")
168         fi.add(path .. "*" .. suffix)
169         fi.add(path .. "*/*" .. suffix)
170         fi.scan()
171         
172         for k, v in pairs(fi.indexes) do
173                 index[v[2]] = v[1]
174         end
175 end
176
177 -- Calls the index function of all available controllers
178 function createindex_plain(path, suffix)
179         local cachetime = nil 
180         
181         local controllers = luci.util.combine(
182                 luci.fs.glob(path .. "*" .. suffix) or {},
183                 luci.fs.glob(path .. "*/*" .. suffix) or {}
184         )
185         
186         if indexcache then
187                 cachetime = luci.fs.mtime(indexcache)
188                 
189                 if not cachetime then
190                         luci.fs.mkdir(indexcache)
191                         luci.fs.chmod(indexcache, "a=,u=rwx")
192                 end
193         end
194
195         if not cachetime then
196                 for i,c in ipairs(controllers) do
197                         c = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".")
198                         stat, mod = pcall(require, c)
199         
200                         if stat and mod and type(mod.index) == "function" then
201                                 index[c] = mod.index
202                                 
203                                 if indexcache then
204                                         luci.fs.writefile(indexcache .. "/" .. c, string.dump(mod.index))
205                                 end
206                         end
207                 end
208         else
209                 for i,c in ipairs(luci.fs.dir(indexcache)) do
210                         if c:sub(1) ~= "." then
211                                 index[c] = loadfile(indexcache .. "/" .. c)
212                         end
213                 end
214         end
215 end
216
217 -- Creates the dispatching tree from the index
218 function createtree()
219         if not built_index then
220                 createindex()
221         end
222
223         for k, v in pairs(index) do
224                 luci.util.updfenv(v, _M)
225                 
226                 local stat, mod = pcall(require, k)
227                 if stat then            
228                         luci.util.updfenv(v, mod)
229                 end
230                 
231                 pcall(v)
232         end
233         
234         built_tree = true
235 end
236
237 -- Shortcut for creating a dispatching node
238 function entry(path, target, title, order, add)
239         add = add or {}
240
241         local c = node(path)
242         c.target = target
243         c.title  = title
244         c.order  = order
245
246         for k,v in pairs(add) do
247                 c[k] = v
248         end
249
250         return c
251 end
252
253 -- Fetch a dispatching node
254 function node(...)
255         local c = tree
256
257         if arg[1] and type(arg[1]) == "table" then
258                 arg = arg[1]
259         end
260
261         for k,v in ipairs(arg) do
262                 if not c.nodes[v] then
263                         c.nodes[v] = {nodes={}}
264                 end
265
266                 c = c.nodes[v]
267         end
268
269         return c
270 end
271
272 -- Subdispatchers --
273 function alias(...)
274         local req = arg
275         return function()
276                 request = req
277                 dispatch()
278         end
279 end
280
281 function template(name)
282         require("luci.template")
283         return function() luci.template.render(name) end
284 end
285
286 function cbi(model)
287         require("luci.cbi")
288         require("luci.template")
289
290         return function()
291                 local stat, res = pcall(luci.cbi.load, model)
292                 if not stat then
293                         error500(res)
294                         return true
295                 end
296
297                 local stat, err = pcall(res.parse, res)
298                 if not stat then
299                         error500(err)
300                         return true
301                 end
302
303                 luci.template.render("cbi/header")
304                 res:render()
305                 luci.template.render("cbi/footer")
306         end
307 end