e769fd7b1b4aa315263d69b57c716b855c98e604
[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