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