* 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