d69045c0245d06365de579bb24ecf9be3384f7f2
[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 -- Global request object
35 request = {}
36
37 -- Active dispatched node
38 dispatched = nil
39
40
41 -- Builds a URL
42 function build_url(...)
43 return luci.http.dispatcher() .. "/" .. table.concat(arg, "/")
44 end
45
46 -- Sends a 404 error code and renders the "error404" template if available
47 function error404(message)
48 luci.http.status(404, "Not Found")
49 message = message or "Not Found"
50
51 require("luci.template")
52 if not pcall(luci.template.render, "error404") then
53 luci.http.prepare_content("text/plain")
54 print(message)
55 end
56 return false
57 end
58
59 -- Sends a 500 error code and renders the "error500" template if available
60 function error500(message)
61 luci.http.status(500, "Internal Server Error")
62
63 require("luci.template")
64 if not pcall(luci.template.render, "error500", {message=message}) then
65 luci.http.prepare_content("text/plain")
66 print(message)
67 end
68 return false
69 end
70
71 -- Dispatches a request depending on the PATH_INFO variable
72 function httpdispatch()
73 local pathinfo = luci.http.env.PATH_INFO or ""
74 local c = tree
75
76 for s in pathinfo:gmatch("([%w_]+)") do
77 table.insert(request, s)
78 end
79
80 dispatch()
81 end
82
83 function dispatch()
84 local c = tree
85 local track = {}
86
87 for i, s in ipairs(request) do
88 c = c.nodes[s]
89 if not c then
90 break
91 end
92
93 for k, v in pairs(c) do
94 track[k] = v
95 end
96 end
97
98
99 if track.i18n then
100 require("luci.i18n").loadc(track.i18n)
101 end
102
103 if track.setgroup then
104 luci.sys.process.setgroup(track.setgroup)
105 end
106
107 if track.setuser then
108 luci.sys.process.setuser(track.setuser)
109 end
110
111 -- Init template engine
112 local tpl = require("luci.template")
113 tpl.viewns.translate = function(...) return require("luci.i18n").translate(...) end
114 tpl.viewns.controller = luci.http.dispatcher()
115 tpl.viewns.uploadctrl = luci.http.dispatcher_upload()
116 tpl.viewns.media = luci.config.main.mediaurlbase
117 tpl.viewns.resource = luci.config.main.resourcebase
118
119
120 if c and type(c.target) == "function" then
121 dispatched = c
122
123 stat, err = pcall(c.target)
124 if not stat then
125 error500(err)
126 end
127 else
128 error404()
129 end
130 end
131
132 -- Generates the dispatching tree
133 function createindex()
134 local path = luci.sys.libpath() .. "/controller/"
135 local suff = ".lua"
136
137 if pcall(require, "fastindex") then
138 createindex_fastindex(path, suff)
139 else
140 createindex_plain(path, suff)
141 end
142 end
143
144 -- Uses fastindex to create the dispatching tree
145 function createindex_fastindex(path, suffix)
146 local fi = fastindex.new("index")
147 fi.add(path .. "*" .. suffix)
148 fi.add(path .. "*/*" .. suffix)
149 fi.scan()
150
151 for k, v in pairs(fi.indexes) do
152 local stat, mod = pcall(require, v[2])
153
154 luci.util.updfenv(v[1], luci.dispatcher)
155 pcall(v[1])
156 end
157 end
158
159 -- Calls the index function of all available controllers
160 function createindex_plain(path, suffix)
161 local controllers = luci.util.combine(
162 luci.fs.glob(path .. "*" .. suffix) or {},
163 luci.fs.glob(path .. "*/*" .. suffix) or {}
164 )
165
166 for i,c in ipairs(controllers) do
167 c = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".")
168 stat, mod = pcall(require, c)
169
170 if stat and mod and type(mod.index) == "function" then
171 luci.util.updfenv(mod.index, luci.dispatcher)
172 pcall(mod.index)
173 end
174 end
175 end
176
177 -- Shortcut for creating a dispatching node
178 function entry(path, target, title, order, add)
179 add = add or {}
180
181 local c = node(path)
182 c.target = target
183 c.title = title
184 c.order = order
185
186 for k,v in pairs(add) do
187 c[k] = v
188 end
189
190 return c
191 end
192
193 -- Fetch a dispatching node
194 function node(...)
195 local c = tree
196
197 if arg[1] and type(arg[1]) == "table" then
198 arg = arg[1]
199 end
200
201 for k,v in ipairs(arg) do
202 if not c.nodes[v] then
203 c.nodes[v] = {nodes={}}
204 end
205
206 c = c.nodes[v]
207 end
208
209 return c
210 end
211
212 -- Subdispatchers --
213 function alias(...)
214 local req = arg
215 return function()
216 request = req
217 dispatch()
218 end
219 end
220
221 function template(name)
222 require("luci.template")
223 return function() luci.template.render(name) end
224 end
225
226 function cbi(model)
227 require("luci.cbi")
228 require("luci.template")
229
230 return function()
231 local stat, res = pcall(luci.cbi.load, model)
232 if not stat then
233 error500(res)
234 return true
235 end
236
237 local stat, err = pcall(res.parse, res)
238 if not stat then
239 error500(err)
240 return true
241 end
242
243 luci.template.render("cbi/header")
244 res:render()
245 luci.template.render("cbi/footer")
246 end
247 end