* 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