fc551a4bdeb54efa83985742e7c83e3998d1f0c8
[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 for k, v in pairs(index) do
225 luci.util.updfenv(v, _M)
226 luci.util.extfenv(v, "_NAME", k)
227 pcall(v)
228 end
229
230 built_tree = true
231 end
232
233 -- Shortcut for creating a dispatching node
234 function entry(path, target, title, order, add)
235 add = add or {}
236
237 local c = node(path)
238 c.target = target
239 c.title = title
240 c.order = order
241 c.module = getfenv(2)._NAME
242
243 for k,v in pairs(add) do
244 c[k] = v
245 end
246
247 return c
248 end
249
250 -- Fetch a dispatching node
251 function node(...)
252 local c = tree
253
254 if arg[1] and type(arg[1]) == "table" then
255 arg = arg[1]
256 end
257
258 for k,v in ipairs(arg) do
259 if not c.nodes[v] then
260 c.nodes[v] = {nodes={}, module=getfenv(2)._NAME}
261 end
262
263 c = c.nodes[v]
264 end
265
266 return c
267 end
268
269 -- Subdispatchers --
270 function alias(...)
271 local req = arg
272 return function()
273 request = req
274 dispatch()
275 end
276 end
277
278 function call(name)
279 return function() getfenv()[name]() end
280 end
281
282 function template(name)
283 require("luci.template")
284 return function() luci.template.render(name) end
285 end
286
287 function cbi(model)
288 require("luci.cbi")
289 require("luci.template")
290
291 return function()
292 local stat, res = pcall(luci.cbi.load, model)
293 if not stat then
294 error500(res)
295 return true
296 end
297
298 local stat, err = pcall(res.parse, res)
299 if not stat then
300 error500(err)
301 return true
302 end
303
304 luci.template.render("cbi/header")
305 res:render()
306 luci.template.render("cbi/footer")
307 end
308 end