* Updated dispatcher to use fastindex if available
[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