bf2ac511e4350543b4c540ace7fd73a066d1b512
[project/luci.git] / src / ffluci / dispatcher.lua
1 --[[
2 FFLuCI - Dispatcher
3
4 Description:
5 The request dispatcher and module dispatcher generators
6
7
8 The dispatching process:
9 For a detailed explanation of the dispatching process we assume:
10 You have installed the FFLuCI CGI-Dispatcher in /cgi-bin/ffluci
11
12 To enforce a higher level of security only the CGI-Dispatcher
13 resides inside the web server's document root, everything else
14 stays inside an external directory, we assume this is /lua/ffluci
15 for this explanation.
16
17 All controllers and action are reachable as sub-objects of /cgi-bin/ffluci
18 as if they were virtual folders and files
19 e.g.: /cgi-bin/ffluci/public/info/about
20 /cgi-bin/ffluci/admin/network/interfaces
21 and so on.
22
23 The PATH_INFO variable holds the dispatch path and
24 will be split into three parts: /category/module/action
25
26 Category: This is the category in which modules are stored in
27 By default there are two categories:
28 "public" - which is the default public category
29 "admin" - which is the default protected category
30
31 As FFLuCI itself does not implement authentication
32 you should make sure that "admin" and other sensitive
33 categories are protected by the webserver.
34
35 E.g. for busybox add a line like:
36 /cgi-bin/ffluci/admin:root:$p$root
37 to /etc/httpd.conf to protect the "admin" category
38
39
40 Module: This is the controller which will handle the request further
41 It is always a submodule of ffluci.controller, so a module
42 called "helloworld" will be stored in
43 /lua/ffluci/controller/helloworld.lua
44 You are free to submodule your controllers any further.
45
46 Action: This is action that will be invoked after loading the module.
47 The kind of how the action will be dispatched depends on
48 the module dispatcher that is defined in the controller.
49 See the description of the default module dispatcher down
50 on this page for some examples.
51
52
53 The main dispatcher at first searches for the module by trying to
54 include ffluci.controller.category.module
55 (where "category" is the category name and "module" is the module name)
56 If this fails a 404 status code will be send to the client and FFLuCI exits
57
58 Then the main dispatcher calls the module dispatcher
59 ffluci.controller.category.module.dispatcher with the request object
60 as the only argument. The module dispatcher is then responsible
61 for the further dispatching process.
62
63
64 FileId:
65 $Id$
66
67 License:
68 Copyright 2008 Steven Barth <steven@midlink.org>
69
70 Licensed under the Apache License, Version 2.0 (the "License");
71 you may not use this file except in compliance with the License.
72 You may obtain a copy of the License at
73
74 http://www.apache.org/licenses/LICENSE-2.0
75
76 Unless required by applicable law or agreed to in writing, software
77 distributed under the License is distributed on an "AS IS" BASIS,
78 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
79 See the License for the specific language governing permissions and
80 limitations under the License.
81
82 ]]--
83
84 module("ffluci.dispatcher", package.seeall)
85 require("ffluci.http")
86 require("ffluci.template")
87
88
89 -- Dispatches the "request"
90 function dispatch(req)
91 request = req
92 local m = "ffluci.controller." .. request.category .. "." .. request.module
93 local stat, module = pcall(require, m)
94 if not stat then
95 return error404()
96 else
97 module.request = request
98 setfenv(module.dispatcher, module)
99 return module.dispatcher(request)
100 end
101 end
102
103 -- Sends a 404 error code and renders the "error404" template if available
104 function error404(message)
105 message = message or "Not Found"
106
107 local s, t = pcall(ffluci.template.Template, "error404")
108
109 if not s then
110 ffluci.http.textheader()
111 print(message)
112 else
113 t:render()
114 end
115 return false
116 end
117
118 -- Sends a 500 error code and renders the "error500" template if available
119 function error500(message)
120 ffluci.http.status(500, "Internal Server Error")
121
122 local s, t = pcall(ffluci.template.Template, "error500")
123
124 if not s then
125 ffluci.http.textheader()
126 print(message)
127 else
128 t:render()
129 end
130 return false
131 end
132
133
134 -- Dispatches a request depending on the PATH_INFO variable
135 function httpdispatch()
136 local pathinfo = os.getenv("PATH_INFO") or ""
137 local parts = pathinfo:gmatch("/[%w-]+")
138
139 local sanitize = function(s, default)
140 return s and s:sub(2) or default
141 end
142
143 local cat = sanitize(parts(), "public")
144 local mod = sanitize(parts(), "index")
145 local act = sanitize(parts(), "index")
146
147 dispatch({category=cat, module=mod, action=act})
148 end
149
150 -- The Simple View Dispatcher directly renders the template
151 -- which is placed in ffluci/views/"request.module"/"request.action"
152 function simpleview(request)
153 local i18n = require("ffluci.i18n")
154 local tmpl = require("ffluci.template")
155 local disp = require("ffluci.dispatcher")
156
157 i18n.loadc(request.module)
158 local s, t = pcall(tmpl.Template, request.module .. "/" .. request.action)
159
160 if not s then
161 disp.error404()
162 else
163 t:render()
164 end
165 end
166
167 -- The Action Dispatcher searches the module for any function called
168 -- action_"request.action" and calls it
169 function action(request)
170 local i18n = require("ffluci.i18n")
171 local disp = require("ffluci.dispatcher")
172
173 i18n.loadc(request.module)
174 local action = getfenv()["action_" .. request.action:gsub("-", "_")]
175 if action then
176 action()
177 else
178 disp.error404()
179 end
180 end