* Reintroduced compiling support
[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 or c.leaf 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         createindex_plain(path, suff)
164         
165         built_index = true
166 end
167
168 -- Uses fastindex to create the dispatching tree
169 function createindex_fastindex(path, suffix)    
170         local fi = fastindex.new("index")
171         fi.add(path .. "*" .. suffix)
172         fi.add(path .. "*/*" .. suffix)
173         fi.scan()
174         
175         for k, v in pairs(fi.indexes) do
176                 index[v[2]] = v[1]
177         end
178 end
179
180 -- Calls the index function of all available controllers
181 function createindex_plain(path, suffix)
182         local cachetime = nil 
183         
184         local controllers = luci.util.combine(
185                 luci.fs.glob(path .. "*" .. suffix) or {},
186                 luci.fs.glob(path .. "*/*" .. suffix) or {}
187         )
188         
189         if indexcache then
190                 cachetime = luci.fs.mtime(indexcache)
191                 
192                 if not cachetime then
193                         luci.fs.mkdir(indexcache)
194                         luci.fs.chmod(indexcache, "a=,u=rwx")
195                 end
196         end
197
198         if not cachetime then
199                 for i,c in ipairs(controllers) do
200                         c = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".")
201                         stat, mod = pcall(require, c)
202         
203                         if stat and mod and type(mod.index) == "function" then
204                                 index[c] = mod.index
205                                 
206                                 if indexcache then
207                                         luci.fs.writefile(indexcache .. "/" .. c, string.dump(mod.index))
208                                 end
209                         end
210                 end
211         else
212                 for i,c in ipairs(luci.fs.dir(indexcache)) do
213                         if c:sub(1) ~= "." then
214                                 index[c] = loadfile(indexcache .. "/" .. c)
215                         end
216                 end
217         end
218 end
219
220 -- Creates the dispatching tree from the index
221 function createtree()
222         if not built_index then
223                 createindex()
224         end
225         
226         require("luci.i18n")
227
228         for k, v in pairs(index) do
229                 luci.util.updfenv(v, _M)
230                 luci.util.extfenv(v, "_NAME", k)
231                 
232                 local stat, err = pcall(v)
233                 if not stat then
234                         error500(err)   
235                         os.exit(1)
236                 end
237         end
238         
239         built_tree = true
240 end
241
242 -- Shortcut for creating a dispatching node
243 function entry(path, target, title, order, add)
244         add = add or {}
245
246         local c = node(path)
247         c.target = target
248         c.title  = title
249         c.order  = order
250         c.module = getfenv(2)._NAME
251
252         for k,v in pairs(add) do
253                 c[k] = v
254         end
255
256         return c
257 end
258
259 -- Fetch a dispatching node
260 function node(...)
261         local c = tree
262
263         if arg[1] and type(arg[1]) == "table" then
264                 arg = arg[1]
265         end
266
267         for k,v in ipairs(arg) do
268                 if not c.nodes[v] then
269                         c.nodes[v] = {nodes={}, module=getfenv(2)._NAME}
270                 end
271
272                 c = c.nodes[v]
273         end
274
275         return c
276 end
277
278 -- Subdispatchers --
279 function alias(...)
280         local req = arg
281         return function()
282                 request = req
283                 dispatch()
284         end
285 end
286
287 function rewrite(n, ...)
288         local req = arg
289         return function()
290                 for i=1,n do 
291                         table.remove(request, 1)
292                 end
293                 
294                 for i,r in ipairs(req) do
295                         table.insert(request, i, r)
296                 end
297                 
298                 dispatch()
299         end
300 end
301
302 function call(name)
303         return function() getfenv()[name]() end
304 end
305
306 function template(name)
307         require("luci.template")
308         return function() luci.template.render(name) end
309 end
310
311 function cbi(model)
312         require("luci.cbi")
313         require("luci.template")
314
315         return function()
316                 local stat, res = pcall(luci.cbi.load, model)
317                 if not stat then
318                         error500(res)
319                         return true
320                 end
321
322                 local stat, err = pcall(res.parse, res)
323                 if not stat then
324                         error500(err)
325                         return true
326                 end
327
328                 luci.template.render("cbi/header")
329                 res:render()
330                 luci.template.render("cbi/footer")
331         end
332 end