* Added support for first-level controllers
[project/luci.git] / core / src / dispatcher.lua
1 --[[
2 FFLuCI - 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("ffluci.dispatcher", package.seeall)
27 require("ffluci.http")
28 require("ffluci.sys")
29 require("ffluci.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 ffluci.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         ffluci.http.status(404, "Not Found")
49         message = message or "Not Found"
50         
51         require("ffluci.template")
52         if not pcall(ffluci.template.render, "error404") then
53                 ffluci.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         ffluci.http.status(500, "Internal Server Error")
62         
63         require("ffluci.template")
64         if not pcall(ffluci.template.render, "error500", {message=message}) then
65                 ffluci.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 = ffluci.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("ffluci.i18n").loadc(track.i18n)
101         end
102         
103         if track.setuser then
104                 ffluci.sys.process.setuser(track.setuser)
105         end
106         
107         if track.setgroup then
108                 ffluci.sys.process.setgroup(track.setgroup)
109         end
110         
111         
112         if c and type(c.target) == "function" then
113                 dispatched = c
114                 
115                 stat, err = pcall(c.target)
116                 if not stat then
117                         error500(err)
118                 end
119         else
120                 error404()
121         end
122 end
123
124
125 -- Calls the index function of all available controllers
126 function createindex()
127         local root = ffluci.sys.libpath() .. "/controller/"
128         local suff = ".lua"
129         
130         local controllers = ffluci.util.combine(
131                 ffluci.fs.glob(root .. "*" .. suff),
132                 ffluci.fs.glob(root .. "*/*" .. suff)
133         )
134         
135         for i,c in ipairs(controllers) do
136                 c = "ffluci.controller." .. c:sub(#root+1, #c-#suff):gsub("/", ".")
137                 stat, mod = pcall(require, c)
138         
139                 if stat and mod and type(mod.index) == "function" then
140                         ffluci.util.updfenv(mod.index, ffluci.dispatcher)
141                         pcall(mod.index)
142                 end
143         end
144 end
145
146 -- Shortcut for creating a dispatching node
147 function entry(path, target, title, order, add)
148         add = add or {}
149
150         local c = node(path)
151         c.target = target
152         c.title  = title
153         c.order  = order
154         
155         for k,v in pairs(add) do
156                 c[k] = v
157         end
158         
159         return c
160 end
161
162 -- Fetch a dispatching node
163 function node(...)
164         local c = tree
165         
166         if arg[1] and type(arg[1]) == "table" then
167                 arg = arg[1]
168         end
169         
170         for k,v in ipairs(arg) do
171                 if not c.nodes[v] then
172                         c.nodes[v] = {nodes={}}
173                 end
174                 
175                 c = c.nodes[v]
176         end
177         
178         return c
179 end
180
181 -- Subdispatchers --
182 function alias(...)
183         local req = arg
184         return function()
185                 request = req
186                 dispatch()
187         end
188 end
189
190 function template(name)
191         require("ffluci.template")
192         return function() ffluci.template.render(name) end
193 end
194
195 function cbi(model)
196         require("ffluci.cbi")
197         require("ffluci.template")
198         
199         return function()
200                 local stat, res = pcall(ffluci.cbi.load, model)
201                 if not stat then
202                         error500(res)
203                         return true
204                 end
205                 
206                 local stat, err = pcall(res.parse, res)
207                 if not stat then
208                         error500(err)
209                         return true
210                 end
211                 
212                 ffluci.template.render("cbi/header")
213                 res:render()
214                 ffluci.template.render("cbi/footer")
215         end
216 end