* luci/core: dispatcher.lua: fix build_url()
[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(arg)
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