* Reworked host-Target for global Makefile
[project/luci.git] / core / src / 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
132         if c and type(c.target) == "function" then
133                 dispatched = c
134
135                 stat, err = pcall(c.target)
136                 if not stat then
137                         error500(err)
138                 end
139         else
140                 error404()
141         end
142 end
143
144 -- Generates the dispatching tree
145 function createindex()
146         index = {}
147         local path = luci.sys.libpath() .. "/controller/"
148         local suff = ".lua"
149         
150         if pcall(require, "fastindex") then
151                 createindex_fastindex(path, suff)
152         else
153                 createindex_plain(path, suff)
154         end
155         
156         built_index = true
157 end
158
159 -- Uses fastindex to create the dispatching tree
160 function createindex_fastindex(path, suffix)    
161         local fi = fastindex.new("index")
162         fi.add(path .. "*" .. suffix)
163         fi.add(path .. "*/*" .. suffix)
164         fi.scan()
165         
166         for k, v in pairs(fi.indexes) do
167                 index[v[2]] = v[1]
168         end
169 end
170
171 -- Calls the index function of all available controllers
172 function createindex_plain(path, suffix)
173         local controllers = luci.util.combine(
174                 luci.fs.glob(path .. "*" .. suffix) or {},
175                 luci.fs.glob(path .. "*/*" .. suffix) or {}
176         )
177
178         for i,c in ipairs(controllers) do
179                 c = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".")
180                 stat, mod = pcall(require, c)
181
182                 if stat and mod and type(mod.index) == "function" then
183                         index[c] = mod.index
184                 end
185         end
186 end
187
188 -- Creates the dispatching tree from the index
189 function createtree()
190         if not built_index then
191                 createindex()
192         end
193
194         for k, v in pairs(index) do
195                 luci.util.updfenv(v, _M)
196                 
197                 local stat, mod = pcall(require, k)
198                 if stat then            
199                         luci.util.updfenv(v, mod)
200                 end
201                 
202                 pcall(v)
203         end
204         
205         built_tree = true
206 end
207
208 -- Shortcut for creating a dispatching node
209 function entry(path, target, title, order, add)
210         add = add or {}
211
212         local c = node(path)
213         c.target = target
214         c.title  = title
215         c.order  = order
216
217         for k,v in pairs(add) do
218                 c[k] = v
219         end
220
221         return c
222 end
223
224 -- Fetch a dispatching node
225 function node(...)
226         local c = tree
227
228         if arg[1] and type(arg[1]) == "table" then
229                 arg = arg[1]
230         end
231
232         for k,v in ipairs(arg) do
233                 if not c.nodes[v] then
234                         c.nodes[v] = {nodes={}}
235                 end
236
237                 c = c.nodes[v]
238         end
239
240         return c
241 end
242
243 -- Subdispatchers --
244 function alias(...)
245         local req = arg
246         return function()
247                 request = req
248                 dispatch()
249         end
250 end
251
252 function template(name)
253         require("luci.template")
254         return function() luci.template.render(name) end
255 end
256
257 function cbi(model)
258         require("luci.cbi")
259         require("luci.template")
260
261         return function()
262                 local stat, res = pcall(luci.cbi.load, model)
263                 if not stat then
264                         error500(res)
265                         return true
266                 end
267
268                 local stat, err = pcall(res.parse, res)
269                 if not stat then
270                         error500(err)
271                         return true
272                 end
273
274                 luci.template.render("cbi/header")
275                 res:render()
276                 luci.template.render("cbi/footer")
277         end
278 end