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