Merge branch 'menu'
[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         for i,c in ipairs(ffluci.fs.glob(root .. "*/*" .. suff)) do
130                 c = "ffluci.controller." .. c:sub(#root+1, #c-#suff):gsub("/", ".", 1)
131                 stat, mod = pcall(require, c)
132         
133                 if stat and mod and type(mod.index) == "function" then
134                         ffluci.util.updfenv(mod.index, ffluci.dispatcher)
135                         pcall(mod.index)
136                 end
137         end
138 end
139
140
141 -- Fetch a dispatching node
142 function node(...)
143         local c = tree
144         
145         for k,v in ipairs(arg) do
146                 if not c.nodes[v] then
147                         c.nodes[v] = {nodes={}}
148                 end
149                 
150                 c = c.nodes[v]
151         end
152         
153         return c
154 end
155
156 -- Subdispatchers --
157 function alias(...)
158         local req = arg
159         return function()
160                 request = req
161                 dispatch()
162         end
163 end
164
165 function template(name)
166         require("ffluci.template")
167         return function() ffluci.template.render(name) end
168 end
169
170 function cbi(model)
171         require("ffluci.cbi")
172         require("ffluci.template")
173         
174         return function()
175                 local stat, res = pcall(ffluci.cbi.load, model)
176                 if not stat then
177                         error500(res)
178                         return true
179                 end
180                 
181                 local stat, err = pcall(res.parse, res)
182                 if not stat then
183                         error500(err)
184                         return true
185                 end
186                 
187                 ffluci.template.render("cbi/header")
188                 res:render()
189                 ffluci.template.render("cbi/footer")
190         end
191 end