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