* new project: ff-luci - Freifunk Lua Configuration Interface
[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
104 -- Sends a 404 error code and renders the "error404" template if available
105 function error404(message)
106         message = message or "Not Found"
107         
108         ffluci.http.status(404, "Not Found")
109         
110         if not pcall(ffluci.template.render, "error404") then
111                 ffluci.http.textheader()
112                 print(message)          
113         end
114         return false    
115 end
116
117 -- Sends a 500 error code and renders the "error500" template if available
118 function error500(message)
119         ffluci.http.status(500, "Internal Server Error")
120         
121         if not pcall(ffluci.template.render, "error500") then
122                 ffluci.http.textheader()
123                 print(message)
124         end
125         return false    
126 end
127
128
129 -- Dispatches a request depending on the PATH_INFO variable
130 function httpdispatch()
131         local pathinfo = os.getenv("PATH_INFO") or ""
132         local parts = pathinfo:gmatch("/[%w-]+")
133         
134         local sanitize = function(s, default)
135                 return s and s:sub(2) or default
136         end
137         
138         local cat = sanitize(parts(), "public")
139         local mod = sanitize(parts(), "index")
140         local act = sanitize(parts(), "index")
141         
142         dispatch({category=cat, module=mod, action=act})
143 end
144
145 -- The Simple View Dispatcher directly renders the template
146 -- which is placed in ffluci/views/"request.module"/"request.action" 
147 function simpleview(request)
148         local i18n = require("ffluci.i18n")
149         local tmpl = require("ffluci.template")
150         local conf = require("ffluci.config")
151         local disp = require("ffluci.dispatcher")
152         
153         pcall(i18n.load, request.module .. "." .. conf.lang)
154         if not pcall(tmpl.get, request.module .. "/" .. request.action) then
155                 disp.error404()
156         else
157                 tmpl.render(request.module .. "/" .. request.action)
158         end
159 end
160
161 -- The Action Dispatcher searches the module for any function called
162 -- action_"request.action" and calls it
163 function action(request)
164         local i18n = require("ffluci.i18n")
165         local conf = require("ffluci.config")
166         local disp = require("ffluci.dispatcher")
167         
168         pcall(i18n.load, request.module .. "." .. conf.lang)
169         local action = getfenv()["action_" .. request.action:gsub("-", "_")]
170         if action then
171                 action()
172         else
173                 disp.error404()
174         end
175 end