* Rewrote Luci to be coroutine-safe allowing the use of non-forking webservers
authorSteven Barth <steven@midlink.org>
Sat, 14 Jun 2008 14:12:12 +0000 (14:12 +0000)
committerSteven Barth <steven@midlink.org>
Sat, 14 Jun 2008 14:12:12 +0000 (14:12 +0000)
* Setting base version to 0.7

27 files changed:
Makefile
NOTICE
applications/luci-splash/luasrc/controller/splash/splash.lua
applications/luci-statistics/luasrc/controller/luci_statistics/luci_statistics.lua
applications/luci-statistics/luasrc/statistics/rrdtool.lua
build/config.mk
build/module.mk
contrib/luaposix/Makefile
contrib/package/luci/Makefile
libs/cbi/luasrc/view/cbi/header.htm
libs/core/luasrc/init.lua
libs/core/luasrc/util.lua
libs/fastindex/Makefile
libs/sgi-haserl/luasrc/sgi/haserl.lua
libs/sgi-haserl/root/www/cgi-bin/luci
libs/sgi-webuci/luasrc/sgi/webuci.lua
libs/sgi-webuci/root/usr/lib/boa/luci.lua
libs/uci/luasrc/model/uci.lua
libs/web/luasrc/dispatcher.lua
libs/web/luasrc/http.lua
libs/web/luasrc/i18n.lua
libs/web/luasrc/template.lua
modules/admin-core/luasrc/view/admin_system/upgrade.htm
modules/admin-core/luasrc/view/error404.htm
modules/freifunk/luasrc/controller/freifunk/luciinfo.lua
themes/fledermaus/luasrc/view/themes/fledermaus/header.htm
themes/openwrt.org/luasrc/view/themes/openwrt.org/header.htm

index 435d59c599b8f908a4cc179e83ac9598d5e8629d..796bb415412f5dea3ba988bb23fb3872b4566c38 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -32,7 +32,7 @@ hostcopy:
        mkdir -p host
        for i in $(MODULES); do cp -a $$i/dist/* host/ -R 2>/dev/null || true; done
        rm -f host/luci
-       ln -s .$(LUCI_INSTALLDIR) host/luci
+       ln -s .$(LUCI_MODULEDIR) host/luci
 
 run: host
        libs/sgi-webuci/host/buildconfig.sh `pwd`/host  > host/etc/boa/boa.conf
diff --git a/NOTICE b/NOTICE
index b9784e4a06cc13d1d5955d4ddb28609c1db8116f..e2c3383e3e5f2b5fe518910f1e3bfd2ffcbe756d 100644 (file)
--- a/NOTICE
+++ b/NOTICE
@@ -1,7 +1,13 @@
 LuCI - Lua Configuration Interface
 Copyright 2008 Steven Barth <steven@midlink.org>
+Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
 Licensed under the Apache License, Version 2.0.
 
+Contains code from:
+BinDecHex      - Copyright 2007 Tim Kelly/Dialectronics
+coxpcall       - Copyright 2005 - Kepler Project (www.keplerproject.org)
+
+
 Luci-Statistics - Statistics for LuCI
 Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
 Licensed under the Apache License, Version 2.0.
index e7448d7fbf6b6ec242c49a45c0a2343993e47c35..617e0f877ce6bdddb428c6e5bf741c1b358fa0f5 100644 (file)
@@ -12,7 +12,7 @@ function index()
 end
 
 function action_activate()
-       local mac = luci.sys.net.ip4mac(luci.http.env.REMOTE_ADDR)
+       local mac = luci.sys.net.ip4mac(luci.http.getenv("REMOTE_ADDR"))
        if mac and luci.http.formvalue("accept") then
                os.execute("luci-splash add "..mac.." >/dev/null 2>&1")
                luci.http.redirect(luci.model.uci.get("freifunk", "community", "homepage"))
index be6430eed21994851d9f8cf413901c46ff926c5d..36c6d9b573bbf52a2294ffae52f50149e6f12140 100644 (file)
@@ -149,8 +149,8 @@ function statistics_render()
        require("luci.model.uci")
 
        local vars  = luci.http.formvalues()
-       local req   = luci.dispatcher.request
-       local path  = luci.dispatcher.dispatched.path
+       local req   = luci.dispatcher.context.request
+       local path  = luci.dispatcher.context.dispatched.path
        local uci   = luci.model.uci
        local spans = luci.util.split( uci.get( "luci_statistics", "collectd_rrdtool", "RRATimespans" ), "%s+", nil, true )
        local span  = vars.timespan or uci.get( "luci_statistics", "rrdtool", "default_timespan" ) or spans[1]
@@ -160,10 +160,10 @@ function statistics_render()
        local images = { }
 
        -- find requested plugin and instance
-        for i, p in ipairs( luci.dispatcher.dispatched.path ) do
-                if luci.dispatcher.dispatched.path[i] == "graph" then
-                        plugin    = luci.dispatcher.dispatched.path[i+1]
-                        instances = { luci.dispatcher.dispatched.path[i+2] }
+        for i, p in ipairs( luci.dispatcher.context.dispatched.path ) do
+                if luci.dispatcher.context.dispatched.path[i] == "graph" then
+                        plugin    = luci.dispatcher.context.dispatched.path[i+1]
+                        instances = { luci.dispatcher.context.dispatched.path[i+2] }
                 end
         end
 
index 69fb5cff6644ca4e2c407da9d9f074da1a7bf095..4335245837a243b210722838a6b42406047042ac 100644 (file)
@@ -500,7 +500,7 @@ function Graph.render( self, plugin, plugin_instance )
 
        -- check for a whole graph handler
        local plugin_def = "luci.statistics.rrdtool.definitions." .. plugin
-       local stat, def = pcall( require, plugin_def )
+       local stat, def = luci.util.copcall( require, plugin_def )
 
        if stat and def and type(def.rrdargs) == "function" then
 
@@ -539,7 +539,7 @@ function Graph.render( self, plugin, plugin_instance )
 
                        -- check for data type handler
                        local dtype_def = plugin_def .. "." .. dtype
-                       local stat, def = pcall( require, dtype_def )
+                       local stat, def = luci.util.copcall( require, dtype_def )
 
                        if stat and def and type(def.rrdargs) == "function" then
 
index 1755974f2e54542a57222d6e80c447f6a176d954..81d3e0836ca3ed8553b2d8ad17638185f7d64799 100644 (file)
@@ -1,4 +1,9 @@
 LUAC = luac
 LUAC_OPTIONS = -s
-LUCI_INSTALLDIR = /usr/lib/lua/luci
+
+LUA_MODULEDIR = /usr/lib/lua
+LUA_LIBRARYDIR = /usr/lib/lua
+
+LUCI_MODULEDIR = $(LUA_MODULEDIR)/luci
+LUCI_LIBRARYDIR = $(LUA_LIBRARYDIR)/luci
 
index 7fe96cac50946955f55e1fb9b64ed9528aac250b..4ef3668c8b04e3f09c2e2a0443c29ee064e924b7 100644 (file)
@@ -12,9 +12,11 @@ compile:
 clean: luaclean
 
 luasource:
-       mkdir -p dist$(LUCI_INSTALLDIR)
+       mkdir -p dist$(LUA_MODULEDIR)
+       mkdir -p dist$(LUCI_MODULEDIR)
        cp -a root/* dist -R 2>/dev/null || true
-       cp -a luasrc/* dist$(LUCI_INSTALLDIR) -R 2>/dev/null || true
+       cp -a luasrc/* dist$(LUCI_MODULEDIR) -R 2>/dev/null || true
+       cp -a lua/* dist$(LUA_MODULEDIR) -R 2>/dev/null || true
        for i in $$(find dist -name .svn); do rm $$i -rf; done  
        
 luacompile: luasource
index ec832cc2d707bb75b2ff44a5bac7239df6e64729..c357f5d2c2e9f9e270a198ba1cb9b790e77d49be 100644 (file)
@@ -34,8 +34,8 @@ $(LUAPOSIX_DIR)/.patched: $(LUAPOSIX_DIR)/.prepared $(LUAPOSIX_PATCHDIR)/series
 
 compile: $(LUAPOSIX_DIR)/.patched
        $(MAKE) -C $(LUAPOSIX_DIR) CC=$(CC) CFLAGS="$(CFLAGS) $(LUA_CFLAGS)" LDFLAGS="$(LDFLAGS) $(LUA_SHLIBS)" OS="$(OS)"
-       mkdir -p dist/usr/lib/lua
-       cp $(LUAPOSIX_DIR)/posix.so dist/usr/lib/lua/
+       mkdir -p dist$(LUA_LIBRARYDIR)
+       cp $(LUAPOSIX_DIR)/posix.so dist$(LUA_LIBRARYDIR)
 
 luasource:
 compile-all: compile
index af662f8cb36f75ca6ee1c03c8a8f3b9b77816ac3..04229dac5f2aadeecb2859caf96252fdcd307a04 100644 (file)
@@ -5,7 +5,7 @@ PKG_SOURCE_URL:=https://dev.leipzig.freifunk.net/svn/ff-luci/$(PKG_BRANCH)
 PKG_REV:=$(shell LC_ALL=C svn info ${PKG_SOURCE_URL} | sed -ne's/^Last Changed Rev: //p')
 
 PKG_NAME:=luci
-PKG_VERSION:=0.6+svn$(PKG_REV)
+PKG_VERSION:=0.7+svn$(PKG_REV)
 PKG_RELEASE:=1
 
 PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
index 0af52b49e17215d2cbb03039ee9509cced19faa4..ca6ef2fdd9ca6919787fd92c2a72b2d248da9596 100644 (file)
@@ -1,5 +1,5 @@
 <%+header%>
-               <form method="post" action="<%=luci.http.env.REQUEST_URI%>">
+               <form method="post" action="<%=luci.http.getenv("REQUEST_URI")%>">
                        <div>
                                <script type="text/javascript" src="<%=resource%>/cbi.js"></script>
                                <input type="hidden" name="cbi.submit" value="1" />
index b390df3ac9d2764723f6e86c620ab4bfd3f1dbd5..b4ecb93c13f842a470b8d5075b959f6fa281e689 100644 (file)
@@ -25,5 +25,5 @@ limitations under the License.
 ]]--
 module("luci", package.seeall)
 
-__version__ = "0.6"
+__version__ = "0.7"
 __appname__ = "LuCI"
index ad53138e60ffeaf451fda2608b16b24ad5d85079..fda4563ad308524222646d3671106ff0a1b3007a 100644 (file)
@@ -36,7 +36,7 @@ function class(base)
                setmetatable(inst, {__index = class})
                
                if inst.__init__ then
-                       local stat, err = pcall(inst.__init__, inst, ...)
+                       local stat, err = copcall(inst.__init__, inst, ...)
                        if not stat then
                                error(err)
                        end
@@ -152,6 +152,12 @@ function pcdata(value)
 end
 
 
+-- Returns an error message to stdout
+function perror(obj)
+       io.stderr:write(tostring(obj) .. "\n")
+end
+
+
 -- Resets the scope of f doing a shallow copy of its scope into a new table
 function resfenv(f)
        setfenv(f, clone(getfenv(f)))
@@ -255,6 +261,34 @@ function strip_bytecode(dump)
 end
 
 
+-- Creates a new threadlocal store
+function threadlocal()
+       local tbl = {}
+       
+       local function get(self, key)
+               local c = coroutine.running()
+               local thread = coxpt[c] or c or 0
+               if not rawget(self, thread) then
+                       rawset(self, thread, {})
+               end
+               return rawget(self, thread)[key]
+       end
+               
+       local function set(self, key, value)
+               local c = coroutine.running()
+               local thread = coxpt[c] or c or 0
+               if not rawget(self, thread) then
+                       rawset(self, thread, {})
+               end
+               rawget(self, thread)[key] = value
+       end
+       
+       setmetatable(tbl, {__index = get, __newindex = set})
+       
+       return tbl
+end
+
+
 -- Removes whitespace from beginning and end of a string
 function trim(str)
        local s = str:gsub("^%s*(.-)%s*$", "%1")
@@ -355,3 +389,46 @@ end
 function vspairs(t)
        return _sortiter( t, function (a,b) return t[a] < t[b] end )
 end
+
+
+-- Coroutine safe xpcall and pcall versions modified for Luci
+-- original version:
+-- coxpcall 1.13 - Copyright 2005 - Kepler Project (www.keplerproject.org)
+local performResume, handleReturnValue
+local oldpcall, oldxpcall = pcall, xpcall
+coxpt = {}
+
+function handleReturnValue(err, co, status, ...)
+    if not status then
+        return false, err(debug.traceback(co, (...)), ...)
+    end
+    if coroutine.status(co) == 'suspended' then
+        return performResume(err, co, coroutine.yield(...))
+    else
+        return true, ...
+    end
+end
+
+function performResume(err, co, ...)
+    return handleReturnValue(err, co, coroutine.resume(co, ...))
+end    
+
+function coxpcall(f, err, ...)
+    local res, co = oldpcall(coroutine.create, f)
+    if not res then
+        local params = {...}
+        local newf = function() return f(unpack(params)) end
+        co = coroutine.create(newf)
+    end
+    local c = coroutine.running()
+    coxpt[co] = coxpt[c] or c or 0
+    return performResume(err, co, ...)
+end
+
+local function id(trace, ...)
+  return ...
+end
+
+function copcall(f, ...)
+    return coxpcall(f, id, ...)
+end
index 566b38d0baff56ef259f09e157e83f4b09973f86..ee1a40ea80fb46b73892aff14a8a4f26fb783094 100644 (file)
@@ -6,8 +6,8 @@ include ../../build/gccconfig.mk
        $(COMPILE) $(LUA_CFLAGS) $(FPIC) -c -o $@ $< 
 
 compile: src/fastindex.o
-       mkdir -p dist$(LUCI_INSTALLDIR)
-       $(LINK) $(SHLIB_FLAGS) -o dist$(LUCI_INSTALLDIR)/fastindex.so src/fastindex.o $(LUA_SHLIBS)
+       mkdir -p dist$(LUCI_LIBRARYDIR)
+       $(LINK) $(SHLIB_FLAGS) -o dist$(LUCI_LIBRARYDIR)/fastindex.so src/fastindex.o $(LUA_SHLIBS)
 
 clean:
        rm -f src/*.o
index 35bc1c9025575eeff5f878842ab76ab79717c243..315b4da73187c06ce584cfd89602c175b61b3f63 100644 (file)
@@ -24,80 +24,49 @@ limitations under the License.
 
 ]]--
 module("luci.sgi.haserl", package.seeall)
-require("luci.fs")
-
--- Environment Table
-luci.http.env = ENV
-
--- Enforces user authentification
-function luci.http.basic_auth(verify_callback, realm)
-       -- Dummy for Haserl
-       return true
-end
-
--- Returns the main dispatcher URL
-function luci.http.dispatcher()
-       return luci.http.env.SCRIPT_NAME or ""
-end
-
--- Returns the upload dispatcher URL
-function luci.http.dispatcher_upload()
-       return luci.http.dispatcher() .. "-upload"
-end
-
--- Returns a table of all COOKIE, GET and POST Parameters
-function luci.http.formvalues()
-       return FORM
-end
-
--- Gets form value from key
-function luci.http.formvalue(key, default)
-       local c = luci.http.formvalues()
+require("luci.http")
+require("luci.util")
+require("luci.dispatcher")
+
+function run()
+       local r = luci.http.Request()
+       r.env = ENV
+       r.request = normalize_table(FORM)
        
-       for match in key:gmatch("[%w-_]+") do
-               c = c[match]
-               if c == nil then
-                       return default
+       local x = coroutine.create(luci.dispatcher.httpdispatch)
+       while coroutine.status(x) ~= "dead" do
+               local res, id, data1, data2 = coroutine.resume(x, r)
+               
+               if not res then
+                       print("Status: 500 Internal Server Error")
+                       print("Content-Type: text/plain\n")
+                       print(id)
+                       break;
+               end
+               
+               if id == 1 then
+                       io.write("Status: " .. tostring(data1) .. " " .. data2 .. "\n")
+               elseif id == 2 then
+                       io.write(data1 .. ": " .. data2 .. "\n")
+               elseif id == 3 then
+                       io.write("\n")
+               elseif id == 4 then
+                       io.write(data1)
                end
        end
-       
-       return c
 end
 
--- Gets a table of values with a certain prefix
-function luci.http.formvaluetable(prefix)
-       return luci.http.formvalue(prefix, {})
-end
-
--- Sends a custom HTTP-Header
-function luci.http.header(key, value)
-       print(key .. ": " .. value)
-end
-
--- Set Content-Type
-function luci.http.prepare_content(type)
-       print("Content-Type: "..type.."\n")
-end
-
--- Asks the browser to redirect to "url"
-function luci.http.redirect(url)
-       luci.http.status(302, "Found")
-       luci.http.header("Location", url)
-       print()
-end
-
--- Returns the path of an uploaded file
--- WARNING! File uploads can be easily spoofed! Do additional sanity checks!
-function luci.http.upload(name)
-       local fpath = luci.http.formvalue(name)
-       local fname = luci.http.formvalue(name .. "_name")
+function normalize_table(table, prefix)
+       prefix = prefix and prefix .. "." or ""
+       local new = {}
        
-       if fpath and fname and luci.fs.isfile(fpath) then
-               return fpath
+       for k,v in pairs(table) do
+               if type(v) == "table" and #v == 0 then
+                       luci.util.update(new, normalize_table(v, prefix .. k))
+               else
+                       new[prefix .. k] = v
+               end
        end
-end
-
--- Sets HTTP-Status-Header
-function luci.http.status(code, message)
-       print("Status: " .. tostring(code) .. " " .. message)
+       
+       return new
 end
index ab2a3e18ff790ff526d0c4b4621cdc8e8c01ceca..fd8e5941a52e71ff7172746d39f17a5bbb83cc67 100755 (executable)
@@ -1,4 +1,4 @@
 #!/usr/bin/haserl --shell=luac
-require("luci.dispatcher")
+require("luci.sgi.haserl")
 luci.dispatcher.indexcache = "/tmp/.luciindex"
-luci.dispatcher.httpdispatch()
\ No newline at end of file
+luci.sgi.haserl.run()
\ No newline at end of file
index a40888e7e83cabec9388f84e2b12e459144387f2..abe279d051084d621f7a8df52cbacb5747fe1f3b 100644 (file)
@@ -24,93 +24,36 @@ limitations under the License.
 
 ]]--
 module("luci.sgi.webuci", package.seeall)
-
-local status_set = false
-
--- Initialize the environment
-function initenv(env, vars)
-       luci.http.env = env
-       luci.http.vars = vars
-end
-
--- Enforces user authentification
-function luci.http.basic_auth(verify_callback, realm)
-       local user = luci.http.env.auth_user
-       local pass = luci.http.env.auth_password
-       realm = realm or ""
+require("luci.http")
+require("luci.util")
+require("luci.dispatcher")
+
+function run(env, vars)
+       local r = luci.http.Request()
+       r.env = env
+       r.request = vars
        
-       if not user or not verify_callback(user, pass) then
-               luci.http.status("401", "Unauthorized")
-               luci.http.header("WWW-Authenticate", string.format('Basic realm="%s"', realm))
-               return false    
-       else
-               return true
-       end
-end
-
--- Returns the main dispatcher URL
-function luci.http.dispatcher()
-       return luci.http.env.SCRIPT_NAME or ""
-end
-
--- Returns the upload dispatcher URL
-function luci.http.dispatcher_upload()
-       -- To be implemented
-end
-
--- Returns a table of all COOKIE, GET and POST Parameters
-function luci.http.formvalues()
-       return luci.http.vars
-end
-
--- Gets form value from key
-function luci.http.formvalue(key, default)
-       return luci.http.formvalues()[key] or default
-end
-
--- Gets a table of values with a certain prefix
-function luci.http.formvaluetable(prefix)
-       local vals = {}
-       prefix = prefix and prefix .. "." or "."
+       local x = coroutine.create(luci.dispatcher.httpdispatch)
        
-       for k, v in pairs(luci.http.formvalues()) do
-               if k:find(prefix, 1, true) == 1 then
-                       vals[k:sub(#prefix + 1)] = v
+       while coroutine.status(x) ~= "dead" do
+               local res, id, data1, data2 = coroutine.resume(x, r)
+
+               if not res then
+                       print(env.SERVER_PROTOCOL .. " 500 Internal Server Error")
+                       print("Content-Type: text/plain\n")
+                       print(id)
+                       break;
                end
+               
+               if id == 1 then
+                       io.write(env.SERVER_PROTOCOL .. " " .. tostring(data1) .. " " .. data2 .. "\n")
+               elseif id == 2 then
+                       io.write(data1 .. ": " .. data2 .. "\n")
+               elseif id == 3 then
+                       io.write("\n")
+               elseif id == 4 then
+                       io.write(data1)
+               end
+               
        end
-       
-       return vals
-end
-
--- Sends a custom HTTP-Header
-function luci.http.header(key, value)
-       print(key .. ": " .. value)
-end
-
--- Set Content-Type
-function luci.http.prepare_content(type)
-       if not status_set then
-               luci.http.status(200, "OK")
-       end
-       
-       print("Content-Type: "..type.."\n")
-end
-
--- Asks the browser to redirect to "url"
-function luci.http.redirect(url)
-       luci.http.status(302, "Found")
-       luci.http.header("Location", url)
-       print()
-end
-
--- Returns the path of an uploaded file
--- WARNING! File uploads can be easily spoofed! Do additional sanity checks!
-function luci.http.upload(name)
-       -- To be implemented
-end
-
--- Sets HTTP-Status-Header
-function luci.http.status(code, message)
-       print(luci.http.env.SERVER_PROTOCOL .. " " .. tostring(code) .. " " .. message)
-       status_set = true
 end
index 39cee1c602b98e3ed2c839fd9db33c857d29e257..879ef19b4f89f7e28956b05a51391453e83e6fe4 100644 (file)
@@ -60,6 +60,5 @@ function handle_req(context)
        env.SERVER_ADDR     = context.server_addr
        env.SCRIPT_NAME     = env.REQUEST_URI:sub(1, #env.REQUEST_URI - #env.PATH_INFO)
 
-       luci.sgi.webuci.initenv(env, vars)
-       luci.dispatcher.httpdispatch()
+       luci.sgi.webuci.run(env, vars)
 end
index bd6aba41a4252b54367e7b825ee18418b400d219..6662943cf57189182312da1b92c2bd875fe5f9e1 100644 (file)
@@ -27,7 +27,7 @@ local uci  = require("uci")
 local util = require("luci.util")
 local setmetatable, rawget, rawset = setmetatable, rawget, rawset
 local error, pairs, ipairs, tostring = error, pairs, ipairs, tostring
-local table, print = table, print
+local table = table
 
 module("luci.model.uci", function(m) setmetatable(m, {__index = uci}) end)
 
index 64355439b20023a66aa3c316f86fa5bc0aada33f..8516c6f1b6e34d322acf22de4b196085a7f16903 100644 (file)
@@ -34,21 +34,10 @@ if (os.time() < 1000000000) then
        os.execute('date -s '..os.date('%m%d%H%M%Y', luci.fs.mtime(luci.sys.libpath() .. "/dispatcher.lua"))..' > /dev/null 2>&1')
 end
 
--- Local dispatch database
-local tree = {nodes={}}
+context = luci.util.threadlocal()
 
 -- Index table
-local index = {}
-
--- Global request object
-request = {}
-
--- Active dispatched node
-dispatched = nil
-
--- Status fields
-built_index = false
-built_tree  = false
+local index = nil
 
 -- Fastindex
 local fi
@@ -64,9 +53,9 @@ function error401(message)
        message = message or "Unauthorized"
 
        require("luci.template")
-       if not pcall(luci.template.render, "error401") then
+       if not luci.util.copcall(luci.template.render, "error401") then
                luci.http.prepare_content("text/plain")
-               print(message)
+               luci.http.write(message)
        end
        return false
 end
@@ -77,9 +66,9 @@ function error404(message)
        message = message or "Not Found"
 
        require("luci.template")
-       if not pcall(luci.template.render, "error404") then
+       if not luci.util.copcall(luci.template.render, "error404") then
                luci.http.prepare_content("text/plain")
-               print(message)
+               luci.http.write(message)
        end
        return false
 end
@@ -89,31 +78,39 @@ function error500(message)
        luci.http.status(500, "Internal Server Error")
 
        require("luci.template")
-       if not pcall(luci.template.render, "error500", {message=message}) then
+       if not luci.util.copcall(luci.template.render, "error500", {message=message}) then
                luci.http.prepare_content("text/plain")
-               print(message)
+               luci.http.write(message)
        end
        return false
 end
 
 -- Creates a request object for dispatching
-function httpdispatch()
-       local pathinfo = luci.http.env.PATH_INFO or ""
+function httpdispatch(request)
+       luci.http.context.request = request
+       context.request = {}
+       local pathinfo = request.env.PATH_INFO or ""
 
        for node in pathinfo:gmatch("[^/]+") do
-               table.insert(request, node)
+               table.insert(context.request, node)
        end
 
-       dispatch()
+       dispatch(context.request)
+       luci.http.close()
 end
 
 -- Dispatches a request
-function dispatch()
-       if not built_tree then
+function dispatch(request)
+       context.path = request
+       
+       require("luci.i18n")
+       luci.i18n.setlanguage(require("luci.config").main.lang)
+       
+       if not context.tree then
                createtree()
        end
-
-       local c = tree
+       
+       local c = context.tree
        local track = {}
 
        for i, s in ipairs(request) do
@@ -131,6 +128,7 @@ function dispatch()
                local accs = track.sysauth
                accs = (type(accs) == "string") and {accs} or accs
                
+               --[[
                local function sysauth(user, password)
                        return (luci.util.contains(accs, user)
                                and luci.sys.user.checkpasswd(user, password)) 
@@ -140,6 +138,7 @@ function dispatch()
                        error401()
                        return
                end
+               ]]--
        end
 
        if track.i18n then
@@ -156,22 +155,24 @@ function dispatch()
        
        -- Init template engine
        local tpl = require("luci.template")
-       tpl.viewns.translate   = function(...) return require("luci.i18n").translate(...) end
-       tpl.viewns.controller  = luci.http.dispatcher()
-       tpl.viewns.uploadctrl  = luci.http.dispatcher_upload() -- DEPRECATED
-       tpl.viewns.media       = luci.config.main.mediaurlbase
-       tpl.viewns.resource    = luci.config.main.resourcebase
-       tpl.viewns.REQUEST_URI = luci.http.env.SCRIPT_NAME .. (luci.http.env.PATH_INFO or "")
+       local viewns = {}
+       tpl.context.viewns = viewns
+       viewns.write       = luci.http.write
+       viewns.translate   = function(...) return require("luci.i18n").translate(...) end
+       viewns.controller  = luci.http.getenv("SCRIPT_NAME")
+       viewns.media       = luci.config.main.mediaurlbase
+       viewns.resource    = luci.config.main.resourcebase
+       viewns.REQUEST_URI = luci.http.getenv("SCRIPT_NAME") .. (luci.http.getenv("PATH_INFO") or "")
        
 
        if c and type(c.target) == "function" then
-               dispatched = c
-               stat, mod = pcall(require, c.module)
+               context.dispatched = c
+               stat, mod = luci.util.copcall(require, c.module)
                if stat then
                        luci.util.updfenv(c.target, mod)
                end
                
-               stat, err = pcall(c.target)
+               stat, err = luci.util.copcall(c.target)
                if not stat then
                        error500(err)
                end
@@ -182,21 +183,20 @@ end
 
 -- Generates the dispatching tree
 function createindex()
-       index = {}
        local path = luci.sys.libpath() .. "/controller/"
        local suff = ".lua"
        
-       if pcall(require, "luci.fastindex") then
+       if luci.util.copcall(require, "luci.fastindex") then
                createindex_fastindex(path, suff)
        else
                createindex_plain(path, suff)
        end
-       
-       built_index = true
 end
 
 -- Uses fastindex to create the dispatching tree
-function createindex_fastindex(path, suffix)   
+function createindex_fastindex(path, suffix)
+       index = {}
+               
        if not fi then
                fi = luci.fastindex.new("index")
                fi.add(path .. "*" .. suffix)
@@ -212,9 +212,7 @@ end
 -- Calls the index function of all available controllers
 -- Fallback for transition purposes / Leave it in as long as it works otherwise throw it away
 function createindex_plain(path, suffix)
-       if built_index then
-               return
-       end
+       index = {}
 
        local cache = nil 
        
@@ -246,7 +244,7 @@ function createindex_plain(path, suffix)
                end
                
                if not cache or stime > ctime then 
-                       stat, mod = pcall(require, module)
+                       stat, mod = luci.util.copcall(require, module)
        
                        if stat and mod and type(mod.index) == "function" then
                                index[module] = mod.index
@@ -263,10 +261,11 @@ end
 
 -- Creates the dispatching tree from the index
 function createtree()
-       if not built_index then
+       if not index then
                createindex()
        end
        
+       context.tree = {nodes={}}
        require("luci.i18n")
                
        -- Load default translation
@@ -283,14 +282,12 @@ function createtree()
                scope._NAME = k
                setfenv(v, scope)
                
-               local stat, err = pcall(v)
+               local stat, err = luci.util.copcall(v)
                if not stat then
                        error500(err)   
                        os.exit(1)
                end
        end
-       
-       built_tree = true
 end
 
 -- Reassigns a node to another position
@@ -302,7 +299,7 @@ function assign(path, clone, title, order)
        obj.title = title
        obj.order = order
        
-       local c = tree
+       local c = context.tree
        for k, v in ipairs(clone) do
                if not c.nodes[v] then
                        c.nodes[v] = {nodes={}}
@@ -330,8 +327,7 @@ end
 
 -- Fetch a dispatching node
 function node(...)
-       local c = tree
-
+       local c = context.tree
        arg.n = nil
        if arg[1] then
                if type(arg[1]) == "table" then
@@ -357,8 +353,7 @@ end
 function alias(...)
        local req = arg
        return function()
-               request = req
-               dispatch()
+               dispatch(req)
        end
 end
 
@@ -366,19 +361,20 @@ function rewrite(n, ...)
        local req = arg
        return function()
                for i=1,n do 
-                       table.remove(request, 1)
+                       table.remove(context.path, 1)
                end
                
                for i,r in ipairs(req) do
-                       table.insert(request, i, r)
+                       table.insert(context.path, i, r)
                end
                
                dispatch()
        end
 end
 
-function call(name)
-       return function() getfenv()[name]() end
+function call(name, ...)
+       local argv = {...}
+       return function() return getfenv()[name](unpack(argv)) end
 end
 
 function template(name)
@@ -391,13 +387,13 @@ function cbi(model)
        require("luci.template")
 
        return function()
-               local stat, res = pcall(luci.cbi.load, model)
+               local stat, res = luci.util.copcall(luci.cbi.load, model)
                if not stat then
                        error500(res)
                        return true
                end
 
-               local stat, err = pcall(res.parse, res)
+               local stat, err = luci.util.copcall(res.parse, res)
                if not stat then
                        error500(err)
                        return true
index 3bff28adde713c5e6fe673e3d42f52fdf4ff8a18..8ee864ac77edeec7c470e5e04f54d515df984b92 100644 (file)
@@ -28,13 +28,141 @@ limitations under the License.
 ]]--
 
 module("luci.http", package.seeall)
+require("luci.util")
+context = luci.util.threadlocal()
 
-if ENV and ENV.HASERLVER then
-       require("luci.sgi.haserl")
-elseif webuci then
-       require("luci.sgi.webuci")
+
+Request = luci.util.class()
+function Request.__init__(self)
+       self.headers = {}
+       self.request = {}
+       self.uploads = {}
+       self.env = {}
+       self.data = ""
+end
+
+function Request.formvalue(self, name, default)
+       return self.request[name] or default
+end
+
+function Request.formvalues(self)
+       return self.request
+end
+
+function Request.formvaluetable(self, prefix)
+       local vals = {}
+       prefix = prefix and prefix .. "." or "."
+       
+       for k, v in pairs(self.request) do
+               if k:find(prefix, 1, true) == 1 then
+                       vals[k:sub(#prefix + 1)] = v
+               end
+       end
+       
+       return vals
+end
+
+function Request.getenv(self, name)
+       return self.env[name]
+end
+
+function Request.upload(self, name)
+       return self.uploads[name]
+end
+
+
+function close()
+       if not context.eoh then
+               context.eoh = true
+               coroutine.yield(3)
+       end
+       
+       if not context.closed then
+               context.closed = true
+               coroutine.yield(5)
+       end
+end
+
+function formvalue(...)
+       return context.request:formvalue(...)
+end
+
+function formvalues(...)
+       return context.request:formvalues(...)
+end
+
+function formvaluetable(...)
+       return context.request:formvaluetable(...)
 end
 
+function getenv(...)
+       return context.request:getenv(...)
+end
+
+function header(key, value)
+       if not context.status then
+               status()
+       end
+       if not context.headers then
+               context.headers = {}
+       end
+       context.headers[key:lower()] = value
+       coroutine.yield(2, key, value)
+end
+
+function prepare_content(mime)
+       header("Content-Type", mime)
+end
+
+function status(code, message)
+       code = code or 200
+       message = message or "OK"
+       context.status = code
+       coroutine.yield(1, code, message)
+end
+
+function write(content)
+       if not content or #content == 0 then
+               return
+       end
+       if not context.eoh then
+               if not context.status then
+                       status()
+               end
+               if not context.headers or not context.headers["content-type"] then
+                       header("Content-Type", "text/html; charset=utf-8")
+               end
+               
+               context.eoh = true
+               coroutine.yield(3)
+       end
+       coroutine.yield(4, content)
+end
+
+
+function basic_auth(realm, errorpage)
+       header("Status", "401 Unauthorized")
+       header("WWW-Authenticate", string.format('Basic realm="%s"', realm or ""))
+       
+       if errorpage then
+               errorpage()
+       end
+       
+       close()
+end
+
+function redirect(url)
+       header("Status", "302 Found")
+       header("Location", url)
+       close()
+end
+
+function upload(...)
+       return context.request:upload(...)
+end
+
+
+
 function build_querystring(table)
        local s="?"
        
index 3ed1ce01716d82e1026e0a40ffdec029d93901f3..35ad0965d0715ed01a8c8e914f70054b45733544 100644 (file)
@@ -30,6 +30,8 @@ require("luci.sys")
 table   = {}
 i18ndir = luci.sys.libpath() .. "/i18n/"
 loaded  = {}
+context = luci.util.threadlocal()
+default = "en"
 
 -- Clears the translation table
 function clear()
@@ -37,13 +39,17 @@ function clear()
 end
 
 -- Loads a translation and copies its data into the global translation table
-function load(file, force)
-       if force or not loaded[file] then
-               local f = loadfile(i18ndir..file..".lua") or loadfile(i18ndir..file)
+function load(file, lang, force)
+       lang = lang or ""
+       if force or not loaded[lang] or not loaded[lang][file] then
+               local f = loadfile(i18ndir .. file .. "." .. lang .. ".lua")
+                or loadfile(i18ndir .. file .. "." .. lang)
                if f then
-                       setfenv(f, table)
+                       table[lang] = table[lang] or {}
+                       setfenv(f, table[lang])
                        f()
-                       loaded[file] = true
+                       loaded[lang] = loaded[lang] or {}
+                       loaded[lang][file] = true
                        return true
                else
                        return false
@@ -55,13 +61,20 @@ end
 
 -- Same as load but autocompletes the filename with .LANG from config.lang
 function loadc(file, force)
-       load(file .. ".en", force)
-       return load(file .. "." .. require("luci.config").main.lang, force)
+       load(file, default, force)
+       return load(file, context.lang, force)
+end
+
+-- Sets the context language
+function setlanguage(lang)
+       context.lang = lang
 end
 
 -- Returns the i18n-value defined by "key" or if there is no such: "default"
 function translate(key, default)
-       return table[key] or default
+       return (table[context.lang] and table[context.lang][key])
+               or (table[default] and table[default][key])
+               or default
 end
 
 -- Translate shourtcut with sprintf/string.format inclusion
index 61e4e39ec97d685d5c4fa5dcbacbe1312d7874d3..29aedcdadd85a467d7d7ff7e20c9d33103d3588d 100644 (file)
@@ -44,9 +44,10 @@ compiler_mode = luci.config.template.compiler_mode or "memory"
 
 
 -- Define the namespace for template modules
+context = luci.util.threadlocal()
+
 viewns = {
-       write      = io.write,
-       include    = function(name) Template(name):render(getfenv(2)) end,      
+       include    = function(name) Template(name):render(getfenv(2)) end,
 }
 
 -- Compiles a given template into an executable Lua module
@@ -113,7 +114,7 @@ end
 -- Oldstyle render shortcut
 function render(name, scope, ...)
        scope = scope or getfenv(2)
-       local s, t = pcall(Template, name)
+       local s, t = luci.util.copcall(Template, name)
        if not s then
                error(t)
        else
@@ -141,9 +142,10 @@ function Template.__init__(self, name)
        self.viewns = {}
        
        -- Copy over from general namespace
-       for k, v in pairs(viewns) do
-               self.viewns[k] = v
-       end     
+       luci.util.update(self.viewns, viewns)
+       if context.viewns then
+               luci.util.update(self.viewns, context.viewns)
+       end
        
        -- If we have a cached template, skip compiling and loading
        if self.template then
index 7e23c5400f0a7705dc517b79fd61485a0e0468fd..cc7438655ceccff3a26150539604f97c60093ffc 100644 (file)
@@ -4,7 +4,7 @@
 <p><%:a_s_flash_upgrade1%></p>
 <br />
 <% if sysupgrade and not ret then %>
-<form method="post" action="<%=uploadctrl%><%=luci.http.env.PATH_INFO%>" enctype="multipart/form-data">
+<form method="post" action="<%=luci.http.getenv("REQUEST_URI")%>" enctype="multipart/form-data">
        <div class="cbi-section-node">
                <div class="cbi-value clear">
                        <div class="cbi-value-title left"><%:a_s_flash_fwimage%></div>
index 60daee2cbdcc61669eef6aa8ba58404d4e7aac32..03f6c54f388bf46a683a967dda5b021b9d3a12cf 100644 (file)
@@ -1,5 +1,5 @@
 <%+header%>
 <h1>404 Not Found</h1>
 <p>Sorry, the object you requested was not found.</p>
-<tt>Unable to dispatch: <%=luci.http.env.PATH_INFO%></tt>
+<tt>Unable to dispatch: <%=luci.http.request.env.PATH_INFO%></tt>
 <%+footer%>
\ No newline at end of file
index 4c24c5732b82fc4ee152b0f72d8e021e9914c008..4ab63a068a1682dbb2bb358e26cc47ccb85de174 100644 (file)
@@ -22,8 +22,8 @@ function action_index()
        luci.http.prepare_content("text/plain")
        
        -- General
-       print("luciinfo.api=1")
-       print("luciinfo.version=" .. tostring(require("luci").__version__))
+       luci.http.write("luciinfo.api=1\n")
+       luci.http.write("luciinfo.version=" .. tostring(require("luci").__version__) .. "\n")
        
        -- Sysinfo
        local s, m, r = luci.sys.sysinfo()
@@ -31,14 +31,14 @@ function action_index()
        dr = dr and luci.sys.net.hexip4(dr.Gateway) or ""
        local l1, l5, l15 = luci.sys.loadavg()
        
-       print("sysinfo.system=" .. sanitize(s))
-       print("sysinfo.cpu=" .. sanitize(m))
-       print("sysinfo.ram=" .. sanitize(r))
-       print("sysinfo.hostname=" .. sanitize(luci.sys.hostname()))
-       print("sysinfo.load1=" .. tostring(l1))
-       print("sysinfo.load5=" .. tostring(l5))
-       print("sysinfo.load15=" .. tostring(l15))
-       print("sysinfo.defaultgw=" .. dr)
+       luci.http.write("sysinfo.system=" .. sanitize(s) .. "\n")
+       luci.http.write("sysinfo.cpu=" .. sanitize(m) .. "\n")
+       luci.http.write("sysinfo.ram=" .. sanitize(r) .. "\n")
+       luci.http.write("sysinfo.hostname=" .. sanitize(luci.sys.hostname()) .. "\n")
+       luci.http.write("sysinfo.load1=" .. tostring(l1) .. "\n")
+       luci.http.write("sysinfo.load5=" .. tostring(l5) .. "\n")
+       luci.http.write("sysinfo.load15=" .. tostring(l15) .. "\n")
+       luci.http.write("sysinfo.defaultgw=" .. dr .. "\n")
 
        
        -- Freifunk
@@ -46,7 +46,7 @@ function action_index()
        for k, v in pairs(ff) do
                        for i, j in pairs(v) do
                                if i:sub(1, 1) ~= "." then
-                                       print("freifunk." .. k .. "." .. i .. "=" .. j)
+                                       luci.http.write("freifunk." .. k .. "." .. i .. "=" .. j .. "\n")
                                end
                        end
        end
index 327e33964bc5ea695f8321d02309dab43b089b19..7f625fd38e0c1a30eadca4df8d17a38cedea1c24 100644 (file)
@@ -1,11 +1,11 @@
 <%
 require("luci.sys")
 local load1, load5, load15 = luci.sys.loadavg()
-local request  = require("luci.dispatcher").request
+local request  = require("luci.dispatcher").context.path
 local category = request[1]
 local tree     = luci.dispatcher.node()
 local cattree  = category and luci.dispatcher.node(category)
-local node     = luci.dispatcher.dispatched 
+local node     = luci.dispatcher.context.dispatched 
 
 local c = tree
 for i,r in ipairs(request) do
index 889fdd01f08a03518813400e970819256ce99e6c..d1b751871508b37ebeed36ec33ac93f2095c1da0 100644 (file)
@@ -1,11 +1,11 @@
 <%
 require("luci.sys")
 local load1, load5, load15 = luci.sys.loadavg()
-local request  = require("luci.dispatcher").request
+local request  = require("luci.dispatcher").context.path
 local category = request[1]
 local tree     = luci.dispatcher.node()
 local cattree  = category and luci.dispatcher.node(category)
-local node     = luci.dispatcher.dispatched 
+local node     = luci.dispatcher.context.dispatched 
 
 local c = tree
 for i,r in ipairs(request) do