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