Merge fixes / improvements
[project/luci.git] / libs / web / luasrc / dispatcher.lua
index 24ce246..71d2a5c 100644 (file)
@@ -74,12 +74,17 @@ end
 -- @param message      Custom error message (optional)#
 -- @return                     false
 function error500(message)
-       luci.http.status(500, "Internal Server Error")
-
-       require("luci.template")
-       if not luci.util.copcall(luci.template.render, "error500", {message=message}) then
+       luci.util.perror(message)
+       if not context.template_header_sent then
+               luci.http.status(500, "Internal Server Error")
                luci.http.prepare_content("text/plain")
                luci.http.write(message)
+       else
+               require("luci.template")
+               if not luci.util.copcall(luci.template.render, "error500", {message=message}) then
+                       luci.http.prepare_content("text/plain")
+                       luci.http.write(message)
+               end
        end
        return false
 end
@@ -111,11 +116,9 @@ function httpdispatch(request)
                table.insert(context.request, node)
        end
 
-       local stat, err = util.copcall(dispatch, context.request)
-       if not stat then
-               luci.util.perror(err)
-               error500(err)
-       end
+       local stat, err = util.coxpcall(function()
+               dispatch(context.request)
+       end, error500)
 
        luci.http.close()
 
@@ -131,10 +134,14 @@ function dispatch(request)
        ctx.urltoken   = ctx.urltoken or {}
 
        local conf = require "luci.config"
+       assert(conf.main,
+               "/etc/config/luci seems to be corrupt, unable to find section 'main'")
+
        local lang = conf.main.lang
        if lang == "auto" then
                local aclang = http.getenv("HTTP_ACCEPT_LANGUAGE") or ""
-               for lpat in aclang:gmatch("[%w]+") do
+               for lpat in aclang:gmatch("[%w-]+") do
+                       lpat = lpat and lpat:gsub("-", "_")
                        if conf.languages[lpat] then
                                lang = lpat
                                break
@@ -257,7 +264,9 @@ function dispatch(request)
                local user
 
                if sdat then
-                       sdat = loadstring(sdat)()
+                       sdat = loadstring(sdat)
+                       setfenv(sdat, {})
+                       sdat = sdat()
                        if not verifytoken or ctx.urltoken.stok == sdat.token then
                                user = sdat.user
                        end
@@ -348,7 +357,7 @@ end
 --- Generate the dispatching index using the best possible strategy.
 function createindex()
        local path = luci.util.libpath() .. "/controller/"
-       local suff = ".lua"
+       local suff = { ".lua", ".lua.gz" }
 
        if luci.util.copcall(require, "luci.fastindex") then
                createindex_fastindex(path, suff)
@@ -359,14 +368,16 @@ end
 
 --- Generate the dispatching index using the fastindex C-indexer.
 -- @param path         Controller base directory
--- @param suffix       Controller file suffix
-function createindex_fastindex(path, suffix)
+-- @param suffixes     Controller file suffixes
+function createindex_fastindex(path, suffixes)
        index = {}
 
        if not fi then
                fi = luci.fastindex.new("index")
-               fi.add(path .. "*" .. suffix)
-               fi.add(path .. "*/*" .. suffix)
+               for _, suffix in ipairs(suffixes) do
+                       fi.add(path .. "*" .. suffix)
+                       fi.add(path .. "*/*" .. suffix)
+               end
        end
        fi.scan()
 
@@ -377,12 +388,16 @@ end
 
 --- Generate the dispatching index using the native file-cache based strategy.
 -- @param path         Controller base directory
--- @param suffix       Controller file suffix
-function createindex_plain(path, suffix)
-       local controllers = util.combine(
-               luci.fs.glob(path .. "*" .. suffix) or {},
-               luci.fs.glob(path .. "*/*" .. suffix) or {}
-       )
+-- @param suffixes     Controller file suffixes
+function createindex_plain(path, suffixes)
+       local controllers = { }
+       for _, suffix in ipairs(suffixes) do
+               controllers = util.combine(
+                       controllers,
+                       luci.fs.glob(path .. "*" .. suffix) or {},
+                       luci.fs.glob(path .. "*/*" .. suffix) or {}
+               )
+       end
 
        if indexcache then
                local cachedate = fs.mtime(indexcache)
@@ -409,7 +424,11 @@ function createindex_plain(path, suffix)
        index = {}
 
        for i,c in ipairs(controllers) do
-               local module = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".")
+               local module = "luci.controller." .. c:sub(#path+1, #c):gsub("/", ".")
+               for _, suffix in ipairs(suffixes) do
+                       module = module:gsub(suffix.."$", "")
+               end
+
                local mod = require(module)
                local idx = mod.index
 
@@ -511,6 +530,14 @@ function entry(path, target, title, order)
        return c
 end
 
+--- Fetch or create a dispatching node without setting the target module or
+-- enabling the node.
+-- @param      ...             Virtual path
+-- @return                     Dispatching tree node
+function get(...)
+       return _create_node({...})
+end
+
 --- Fetch or create a new dispatching node.
 -- @param      ...             Virtual path
 -- @return                     Dispatching tree node
@@ -588,7 +615,7 @@ end
 
 
 local function _call(self, ...)
-       if #self.argv > 0 then 
+       if #self.argv > 0 then
                return getfenv()[self.name](unpack(self.argv), ...)
        else
                return getfenv()[self.name](...)
@@ -625,27 +652,29 @@ local function _cbi(self, ...)
        local state = nil
 
        for i, res in ipairs(maps) do
-               if config.autoapply then
-                       res.autoapply = config.autoapply
-               end
+               res.flow = config
                local cstate = res:parse()
-               if not state or cstate < state then
+               if cstate and (not state or cstate < state) then
                        state = cstate
                end
        end
 
+       local function _resolve_path(path)
+               return type(path) == "table" and build_url(unpack(path)) or path
+       end
+
        if config.on_valid_to and state and state > 0 and state < 2 then
-               http.redirect(config.on_valid_to)
+               http.redirect(_resolve_path(config.on_valid_to))
                return
        end
 
        if config.on_changed_to and state and state > 1 then
-               http.redirect(config.on_changed_to)
+               http.redirect(_resolve_path(config.on_changed_to))
                return
        end
 
        if config.on_success_to and state and state > 0 then
-               http.redirect(config.on_success_to)
+               http.redirect(_resolve_path(config.on_success_to))
                return
        end
 
@@ -657,14 +686,18 @@ local function _cbi(self, ...)
 
        local pageaction = true
        http.header("X-CBI-State", state or 0)
-       tpl.render("cbi/header", {state = state})
+       if not config.noheader then
+               tpl.render("cbi/header", {state = state})
+       end
        for i, res in ipairs(maps) do
                res:render()
                if res.pageaction == false then
                        pageaction = false
                end
        end
-       tpl.render("cbi/footer", {pageaction=pageaction, state = state, autoapply = config.autoapply})
+       if not config.nofooter then
+               tpl.render("cbi/footer", {flow = config, pageaction=pageaction, state = state, autoapply = config.autoapply})
+       end
 end
 
 --- Create a CBI model dispatching target.
@@ -699,7 +732,7 @@ local function _form(self, ...)
 
        for i, res in ipairs(maps) do
                local cstate = res:parse()
-               if not state or cstate < state then
+               if cstate and (not state or cstate < state) then
                        state = cstate
                end
        end