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