From 6604399aa8f35d33c53a5e5a1fea765f401aef5e Mon Sep 17 00:00:00 2001 From: Steven Barth Date: Thu, 22 May 2008 14:04:03 +0000 Subject: Merge branch 'menu' --- core/src/dispatcher.lua | 309 ++++++++++++++++-------------------------------- 1 file changed, 100 insertions(+), 209 deletions(-) (limited to 'core/src/dispatcher.lua') diff --git a/core/src/dispatcher.lua b/core/src/dispatcher.lua index 0a66ccd3ad..84c665edab 100644 --- a/core/src/dispatcher.lua +++ b/core/src/dispatcher.lua @@ -4,63 +4,6 @@ FFLuCI - Dispatcher Description: The request dispatcher and module dispatcher generators - -The dispatching process: - For a detailed explanation of the dispatching process we assume: - You have installed the FFLuCI CGI-Dispatcher in /cgi-bin/ffluci - - To enforce a higher level of security only the CGI-Dispatcher - resides inside the web server's document root, everything else - stays inside an external directory, we assume this is /lua/ffluci - for this explanation. - - All controllers and action are reachable as sub-objects of /cgi-bin/ffluci - as if they were virtual folders and files - e.g.: /cgi-bin/ffluci/public/info/about - /cgi-bin/ffluci/admin/network/interfaces - and so on. - - The PATH_INFO variable holds the dispatch path and - will be split into three parts: /category/module/action - - Category: This is the category in which modules are stored in - By default there are two categories: - "public" - which is the default public category - "admin" - which is the default protected category - - As FFLuCI itself does not implement authentication - you should make sure that "admin" and other sensitive - categories are protected by the webserver. - - E.g. for busybox add a line like: - /cgi-bin/ffluci/admin:root:$p$root - to /etc/httpd.conf to protect the "admin" category - - - Module: This is the controller which will handle the request further - It is always a submodule of ffluci.controller, so a module - called "helloworld" will be stored in - /lua/ffluci/controller/helloworld.lua - You are free to submodule your controllers any further. - - Action: This is action that will be invoked after loading the module. - The kind of how the action will be dispatched depends on - the module dispatcher that is defined in the controller. - See the description of the default module dispatcher down - on this page for some examples. - - - The main dispatcher at first searches for the module by trying to - include ffluci.controller.category.module - (where "category" is the category name and "module" is the module name) - If this fails a 404 status code will be send to the client and FFLuCI exits - - Then the main dispatcher calls the module dispatcher - ffluci.controller.category.module.dispatcher with the request object - as the only argument. The module dispatcher is then responsible - for the further dispatching process. - - FileId: $Id$ @@ -80,48 +23,24 @@ See the License for the specific language governing permissions and limitations under the License. ]]-- - module("ffluci.dispatcher", package.seeall) require("ffluci.http") -require("ffluci.template") -require("ffluci.config") require("ffluci.sys") +require("ffluci.fs") --- Sets privilege for given category -function assign_privileges(category) - local cp = ffluci.config.category_privileges - if cp and cp[category] then - local u, g = cp[category]:match("([^:]+):([^:]+)") - ffluci.sys.process.setuser(u) - ffluci.sys.process.setgroup(g) - end -end +-- Local dispatch database +local tree = {nodes={}} +-- Global request object +request = {} --- Builds a URL from a triple of category, module and action -function build_url(category, module, action) - category = category or "public" - module = module or "index" - action = action or "index" - - local pattern = ffluci.http.dispatcher() .. "/%s/%s/%s" - return pattern:format(category, module, action) -end +-- Active dispatched node +dispatched = nil --- Dispatches the "request" -function dispatch(req) - request = req - local m = "ffluci.controller." .. request.category .. "." .. request.module - local stat, module = pcall(require, m) - if not stat then - return error404() - else - module.request = request - module.dispatcher = module.dispatcher or dynamic - setfenv(module.dispatcher, module) - return module.dispatcher(request) - end +-- Builds a URL +function build_url(...) + return ffluci.http.dispatcher() .. "/" .. table.concat(arg, "/") end -- Sends a 404 error code and renders the "error404" template if available @@ -129,6 +48,7 @@ function error404(message) ffluci.http.status(404, "Not Found") message = message or "Not Found" + require("ffluci.template") if not pcall(ffluci.template.render, "error404") then ffluci.http.prepare_content("text/plain") print(message) @@ -140,6 +60,7 @@ end function error500(message) ffluci.http.status(500, "Internal Server Error") + require("ffluci.template") if not pcall(ffluci.template.render, "error500", {message=message}) then ffluci.http.prepare_content("text/plain") print(message) @@ -147,154 +68,124 @@ function error500(message) return false end - -- Dispatches a request depending on the PATH_INFO variable function httpdispatch() local pathinfo = ffluci.http.env.PATH_INFO or "" - local parts = pathinfo:gmatch("/[%w-]+") + local c = tree - local sanitize = function(s, default) - return s and s:sub(2) or default + for s in pathinfo:gmatch("/([%w-]+)") do + table.insert(request, s) end - local cat = sanitize(parts(), "public") - local mod = sanitize(parts(), "index") - local act = sanitize(parts(), "index") - - assign_privileges(cat) - dispatch({category=cat, module=mod, action=act}) -end - - --- Dispatchers -- - - --- The Action Dispatcher searches the module for any function called --- action_"request.action" and calls it -function action(...) - local disp = require("ffluci.dispatcher") - if not disp._action(...) then - disp.error404() - end + dispatch() end --- The CBI dispatcher directly parses and renders the CBI map which is --- placed in ffluci/modles/cbi/"request.module"/"request.action" -function cbi(...) - local disp = require("ffluci.dispatcher") - if not disp._cbi(...) then - disp.error404() +function dispatch() + local c = tree + local track = {} + + for i, s in ipairs(request) do + c = c.nodes[s] + if not c then + break + end + + for k, v in pairs(c) do + track[k] = v + end end -end - --- The dynamic dispatcher chains the action, submodule, simpleview and CBI dispatcher --- in this particular order. It is the default dispatcher. -function dynamic(...) - local disp = require("ffluci.dispatcher") - if not disp._action(...) - and not disp._submodule(...) - and not disp._simpleview(...) - and not disp._cbi(...) then - disp.error404() + + + if track.i18n then + require("ffluci.i18n").loadc(track.i18n) end -end - --- The Simple View Dispatcher directly renders the template --- which is placed in ffluci/views/"request.module"/"request.action" -function simpleview(...) - local disp = require("ffluci.dispatcher") - if not disp._simpleview(...) then - disp.error404() + + if track.setuser then + ffluci.sys.process.setuser(track.setuser) end -end - - --- The submodule dispatcher tries to load a submodule of the controller --- and calls its "action"-function -function submodule(...) - local disp = require("ffluci.dispatcher") - if not disp._submodule(...) then - disp.error404() + + if track.setgroup then + ffluci.sys.process.setgroup(track.setgroup) end -end - - --- Internal Dispatcher Functions -- - -function _action(request) - local action = getfenv(2)["action_" .. request.action:gsub("-", "_")] - local i18n = require("ffluci.i18n") - if action then - i18n.loadc(request.category .. "_" .. request.module) - i18n.loadc(request.category .. "_" .. request.module .. "_" .. request.action) - action() - return true + + if c and type(c.target) == "function" then + dispatched = c + + stat, err = pcall(c.target) + if not stat then + error500(err) + end else - return false + error404() end end -function _cbi(request) - local disp = require("ffluci.dispatcher") - local tmpl = require("ffluci.template") - local cbi = require("ffluci.cbi") - local i18n = require("ffluci.i18n") - - local path = request.category.."_"..request.module.."/"..request.action +-- Calls the index function of all available controllers +function createindex() + local root = ffluci.sys.libpath() .. "/controller/" + local suff = ".lua" + for i,c in ipairs(ffluci.fs.glob(root .. "*/*" .. suff)) do + c = "ffluci.controller." .. c:sub(#root+1, #c-#suff):gsub("/", ".", 1) + stat, mod = pcall(require, c) - local stat, map = pcall(cbi.load, path) - if stat and map then - local stat, err = pcall(map.parse, map) - if not stat then - disp.error500(err) - return true + if stat and mod and type(mod.index) == "function" then + ffluci.util.updfenv(mod.index, ffluci.dispatcher) + pcall(mod.index) end - i18n.loadc(request.category .. "_" .. request.module) - i18n.loadc(request.category .. "_" .. request.module .. "_" .. request.action) - tmpl.render("cbi/header") - map:render() - tmpl.render("cbi/footer") - return true - elseif not stat then - disp.error500(map) - return true - else - return false end end -function _simpleview(request) - local i18n = require("ffluci.i18n") - local tmpl = require("ffluci.template") +-- Fetch a dispatching node +function node(...) + local c = tree - local path = request.category.."_"..request.module.."/"..request.action + for k,v in ipairs(arg) do + if not c.nodes[v] then + c.nodes[v] = {nodes={}} + end + + c = c.nodes[v] + end - local stat, t = pcall(tmpl.Template, path) - if stat then - i18n.loadc(request.category .. "_" .. request.module) - i18n.loadc(request.category .. "_" .. request.module .. "_" .. request.action) - t:render() - return true - else - return false + return c +end + +-- Subdispatchers -- +function alias(...) + local req = arg + return function() + request = req + dispatch() end end +function template(name) + require("ffluci.template") + return function() ffluci.template.render(name) end +end -function _submodule(request) - local i18n = require("ffluci.i18n") - local m = "ffluci.controller." .. request.category .. "." .. - request.module .. "." .. request.action - local stat, module = pcall(require, m) +function cbi(model) + require("ffluci.cbi") + require("ffluci.template") - if stat and module.action then - i18n.loadc(request.category .. "_" .. request.module) - i18n.loadc(request.category .. "_" .. request.module .. "_" .. request.action) - return pcall(module.action) + return function() + local stat, res = pcall(ffluci.cbi.load, model) + if not stat then + error500(res) + return true + end + + local stat, err = pcall(res.parse, res) + if not stat then + error500(err) + return true + end + + ffluci.template.render("cbi/header") + res:render() + ffluci.template.render("cbi/footer") end - - return false end \ No newline at end of file -- cgit v1.2.3