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