treewide: separate Lua runtime resources
authorJo-Philipp Wich <redacted>
Tue, 13 Sep 2022 21:50:12 +0000 (23:50 +0200)
committerJo-Philipp Wich <redacted>
Mon, 24 Oct 2022 23:03:37 +0000 (01:03 +0200)
Move classes required for Lua runtime support into a new `luci-lua-runtime`
package. Also replace the `luci.http` and `luci.util` classes in
`luci-lib-base` with stubbed versions interacting with the ucode based
runtime environment.

Finally merge `luci-base-ucode` into the remainders of `luci-base`.

Signed-off-by: Jo-Philipp Wich <redacted>
86 files changed:
.gitignore
build/mkbasepot.sh
build/zoneinfo2ucode.pl
libs/luci-lib-base/luasrc/http.lua
libs/luci-lib-base/luasrc/util.lua
modules/luci-base-ucode/Makefile [deleted file]
modules/luci-base-ucode/htdocs/cgi-bin/luci-ucode [deleted file]
modules/luci-base-ucode/luasrc/ucodebridge/luci/http.lua [deleted file]
modules/luci-base-ucode/luasrc/ucodebridge/luci/util.lua [deleted file]
modules/luci-base-ucode/src/Makefile [deleted file]
modules/luci-base/Makefile
modules/luci-base/htdocs/cgi-bin/luci
modules/luci-base/luasrc/controller/admin/index.lua [deleted file]
modules/luci-base/luasrc/controller/admin/uci.lua [deleted file]
modules/luci-base/luasrc/dispatcher.lua [deleted file]
modules/luci-base/luasrc/dispatcher.luadoc [deleted file]
modules/luci-base/luasrc/template.lua [deleted file]
modules/luci-base/luasrc/view/csrftoken.htm [deleted file]
modules/luci-base/luasrc/view/error404.htm [deleted file]
modules/luci-base/luasrc/view/error500.htm [deleted file]
modules/luci-base/luasrc/view/footer.htm [deleted file]
modules/luci-base/luasrc/view/header.htm [deleted file]
modules/luci-base/luasrc/view/sysauth.htm [deleted file]
modules/luci-base/luasrc/view/view.htm [deleted file]
modules/luci-base/root/usr/libexec/rpcd/luci [deleted file]
modules/luci-base/root/usr/share/luci/menu.d/luci-base.json
modules/luci-base/root/usr/share/rpcd/ucode/luci [moved from modules/luci-base-ucode/root/usr/share/rpcd/ucode/luci with 100% similarity]
modules/luci-base/src/Makefile
modules/luci-base/src/lib/lmo.c [moved from modules/luci-base-ucode/src/lib/lmo.c with 100% similarity]
modules/luci-base/src/lib/lmo.h [moved from modules/luci-base-ucode/src/lib/lmo.h with 100% similarity]
modules/luci-base/src/lib/luci.c [moved from modules/luci-base-ucode/src/lib/luci.c with 100% similarity]
modules/luci-base/src/lib/plural_formula.y [moved from modules/luci-base-ucode/src/lib/plural_formula.y with 100% similarity]
modules/luci-base/src/po2lmo.c
modules/luci-base/ucode/controller/admin/index.uc [moved from modules/luci-base-ucode/ucode/controller/admin/index.uc with 100% similarity]
modules/luci-base/ucode/controller/admin/uci.uc [moved from modules/luci-base-ucode/ucode/controller/admin/uci.uc with 100% similarity]
modules/luci-base/ucode/dispatcher.uc [moved from modules/luci-base-ucode/ucode/dispatcher.uc with 100% similarity]
modules/luci-base/ucode/http.uc [moved from modules/luci-base-ucode/ucode/http.uc with 100% similarity]
modules/luci-base/ucode/runtime.uc [moved from modules/luci-base-ucode/ucode/runtime.uc with 97% similarity]
modules/luci-base/ucode/sys.uc [moved from modules/luci-base-ucode/ucode/sys.uc with 100% similarity]
modules/luci-base/ucode/template/csrftoken.ut [moved from modules/luci-base-ucode/ucode/template/csrftoken.ut with 100% similarity]
modules/luci-base/ucode/template/error404.ut [moved from modules/luci-base-ucode/ucode/template/error404.ut with 100% similarity]
modules/luci-base/ucode/template/error500.ut [moved from modules/luci-base-ucode/ucode/template/error500.ut with 100% similarity]
modules/luci-base/ucode/template/footer.ut [moved from modules/luci-base-ucode/ucode/template/footer.ut with 100% similarity]
modules/luci-base/ucode/template/header.ut [moved from modules/luci-base-ucode/ucode/template/header.ut with 100% similarity]
modules/luci-base/ucode/template/sysauth.ut [moved from modules/luci-base-ucode/ucode/template/sysauth.ut with 100% similarity]
modules/luci-base/ucode/template/view.ut [moved from modules/luci-base-ucode/ucode/template/view.ut with 100% similarity]
modules/luci-base/ucode/uhttpd.uc [moved from modules/luci-base-ucode/ucode/uhttpd.uc with 100% similarity]
modules/luci-base/ucode/zoneinfo.uc [moved from modules/luci-base-ucode/ucode/zoneinfo.uc with 100% similarity]
modules/luci-compat/Makefile
modules/luci-lua-runtime/Makefile [new file with mode: 0644]
modules/luci-lua-runtime/luasrc/cacheloader.lua [moved from modules/luci-base/luasrc/cacheloader.lua with 100% similarity]
modules/luci-lua-runtime/luasrc/ccache.lua [moved from modules/luci-base/luasrc/ccache.lua with 100% similarity]
modules/luci-lua-runtime/luasrc/config.lua [moved from modules/luci-base/luasrc/config.lua with 100% similarity]
modules/luci-lua-runtime/luasrc/dispatcher.lua [moved from modules/luci-base-ucode/luasrc/ucodebridge/luci/dispatcher.lua with 100% similarity]
modules/luci-lua-runtime/luasrc/i18n.lua [moved from modules/luci-base/luasrc/i18n.lua with 100% similarity]
modules/luci-lua-runtime/luasrc/i18n.luadoc [moved from modules/luci-base/luasrc/i18n.luadoc with 100% similarity]
modules/luci-lua-runtime/luasrc/model/uci.lua [moved from modules/luci-base/luasrc/model/uci.lua with 100% similarity]
modules/luci-lua-runtime/luasrc/model/uci.luadoc [moved from modules/luci-base/luasrc/model/uci.luadoc with 100% similarity]
modules/luci-lua-runtime/luasrc/sgi/cgi.lua [moved from modules/luci-base/luasrc/sgi/cgi.lua with 100% similarity]
modules/luci-lua-runtime/luasrc/sgi/uhttpd.lua [moved from modules/luci-base/luasrc/sgi/uhttpd.lua with 100% similarity]
modules/luci-lua-runtime/luasrc/store.lua [moved from modules/luci-base/luasrc/store.lua with 100% similarity]
modules/luci-lua-runtime/luasrc/sys.lua [moved from modules/luci-base/luasrc/sys.lua with 100% similarity]
modules/luci-lua-runtime/luasrc/sys.luadoc [moved from modules/luci-base/luasrc/sys.luadoc with 100% similarity]
modules/luci-lua-runtime/luasrc/sys/zoneinfo.lua [moved from modules/luci-base/luasrc/sys/zoneinfo.lua with 100% similarity]
modules/luci-lua-runtime/luasrc/sys/zoneinfo/tzdata.lua [moved from modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua with 100% similarity]
modules/luci-lua-runtime/luasrc/sys/zoneinfo/tzoffset.lua [moved from modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua with 100% similarity]
modules/luci-lua-runtime/luasrc/template.lua [moved from modules/luci-base-ucode/luasrc/ucodebridge/luci/template.lua with 100% similarity]
modules/luci-lua-runtime/luasrc/ucodebridge.lua [moved from modules/luci-base-ucode/luasrc/ucodebridge/luci/ucodebridge.lua with 100% similarity]
modules/luci-lua-runtime/luasrc/version.lua [moved from modules/luci-base/luasrc/version.lua with 100% similarity]
modules/luci-lua-runtime/luasrc/view/empty_node_placeholder.htm [moved from modules/luci-base/luasrc/view/empty_node_placeholder.htm with 100% similarity]
modules/luci-lua-runtime/luasrc/view/indexer.htm [moved from modules/luci-base/luasrc/view/indexer.htm with 100% similarity]
modules/luci-lua-runtime/luasrc/xml.lua [moved from modules/luci-base/luasrc/xml.lua with 100% similarity]
modules/luci-lua-runtime/luasrc/xml.luadoc [moved from modules/luci-base/luasrc/xml.luadoc with 100% similarity]
modules/luci-lua-runtime/src/Makefile [new file with mode: 0644]
modules/luci-lua-runtime/src/contrib/lemon.c [moved from modules/luci-base-ucode/src/contrib/lemon.c with 100% similarity]
modules/luci-lua-runtime/src/contrib/lempar.c [moved from modules/luci-base-ucode/src/contrib/lempar.c with 100% similarity]
modules/luci-lua-runtime/src/mkversion.sh [moved from modules/luci-base/src/mkversion.sh with 100% similarity]
modules/luci-lua-runtime/src/plural_formula.y [moved from modules/luci-base/src/plural_formula.y with 100% similarity]
modules/luci-lua-runtime/src/template_lmo.c [moved from modules/luci-base/src/template_lmo.c with 100% similarity]
modules/luci-lua-runtime/src/template_lmo.h [moved from modules/luci-base/src/template_lmo.h with 100% similarity]
modules/luci-lua-runtime/src/template_lualib.c [moved from modules/luci-base/src/template_lualib.c with 100% similarity]
modules/luci-lua-runtime/src/template_lualib.h [moved from modules/luci-base/src/template_lualib.h with 100% similarity]
modules/luci-lua-runtime/src/template_parser.c [moved from modules/luci-base/src/template_parser.c with 100% similarity]
modules/luci-lua-runtime/src/template_parser.h [moved from modules/luci-base/src/template_parser.h with 100% similarity]
modules/luci-lua-runtime/src/template_utils.c [moved from modules/luci-base/src/template_utils.c with 100% similarity]
modules/luci-lua-runtime/src/template_utils.h [moved from modules/luci-base/src/template_utils.h with 100% similarity]

index 60dd7e18d6becf25a9203ee24e17305c6facd1be..7834b9bce4d4a35f4da94764c5e24ae9fd67e545 100644 (file)
@@ -10,7 +10,10 @@ package-lock.json
 modules/luci-base/src/po2lmo
 modules/luci-base/src/jsmin
 modules/luci-base/src/contrib/lemon
-modules/luci-base/src/plural_formula.c
-modules/luci-base/src/plural_formula.h
+modules/luci-base/src/ucode/plural_formula.c
+modules/luci-base/src/ucode/plural_formula.h
+modules/luci-compat/src/contrib/lemon
+modules/luci-compat/src/plural_formula.c
+modules/luci-compat/src/plural_formula.h
 docs/jsapi/*
 !docs/jsapi/README.md
index 0f9247536b86f7e6f69f70f7456b8e3b6f1977dd..d59a151d6a08760d479cc97010c10444c6d45466 100755 (executable)
@@ -8,7 +8,7 @@
 echo -n "Updating modules/luci-base/po/templates/base.pot ... "
 
 ./build/i18n-scan.pl \
-       modules/luci-base/ modules/luci-compat/ modules/luci-mod-admin-full/ \
+       modules/luci-base/ modules/luci-compat/ modules/luci-lua-runtime/ \
        modules/luci-mod-network modules/luci-mod-status modules/luci-mod-system/ \
        protocols/ themes/ \
 > modules/luci-base/po/templates/base.pot
index 35dfac379777b0d041e649f2d80a662d79b50626..941255f2f47dcee3b70a8b0d93df288faee8f00a 100755 (executable)
@@ -7,7 +7,7 @@ use strict;
 my %TZ;
 
 my $tzdin  = $ARGV[0] || "/usr/share/zoneinfo";
-my $tzdout = $ARGV[1] || "./modules/luci-base-ucode/ucode/zoneinfo.uc";
+my $tzdout = $ARGV[1] || "./modules/luci-base/ucode/zoneinfo.uc";
 
 local $/ = "\012";
 open( ZTAB, "< $tzdin/zone.tab" ) || die "open($tzdin/zone.tab): $!";
index 20b55f2854ff3304de29748131472a1419c66f4c..06547ae2ce378364723aa7b3428b2343d642367b 100644 (file)
@@ -6,234 +6,66 @@ local util  = require "luci.util"
 local coroutine = require "coroutine"
 local table = require "table"
 local lhttp = require "lucihttp"
-local nixio = require "nixio"
-local ltn12 = require "luci.ltn12"
 
-local table, ipairs, pairs, type, tostring, tonumber, error =
-       table, ipairs, pairs, type, tostring, tonumber, error
+local L, table, ipairs, pairs, type, error = _G.L, table, ipairs, pairs, type, error
 
 module "luci.http"
 
 HTTP_MAX_CONTENT      = 1024*100               -- 100 kB maximum content size
 
-context = util.threadlocal()
-
-Request = util.class()
-function Request.__init__(self, env, sourcein, sinkerr)
-       self.input = sourcein
-       self.error = sinkerr
-
-
-       -- File handler nil by default to let .content() work
-       self.filehandler = nil
-
-       -- HTTP-Message table
-       self.message = {
-               env = env,
-               headers = {},
-               params = urldecode_params(env.QUERY_STRING or ""),
-       }
-
-       self.parsed_input = false
-end
-
-function Request.formvalue(self, name, noparse)
-       if not noparse and not self.parsed_input then
-               self:_parse_input()
-       end
-
-       if name then
-               return self.message.params[name]
-       else
-               return self.message.params
-       end
-end
-
-function Request.formvaluetable(self, prefix)
-       local vals = {}
-       prefix = prefix and prefix .. "." or "."
-
-       if not self.parsed_input then
-               self:_parse_input()
-       end
-
-       local void = self.message.params[nil]
-       for k, v in pairs(self.message.params) do
-               if k:find(prefix, 1, true) == 1 then
-                       vals[k:sub(#prefix + 1)] = tostring(v)
-               end
-       end
-
-       return vals
-end
-
-function Request.content(self)
-       if not self.parsed_input then
-               self:_parse_input()
-       end
-
-       return self.message.content, self.message.content_length
-end
-
-function Request.getcookie(self, name)
-       return lhttp.header_attribute("cookie; " .. (self:getenv("HTTP_COOKIE") or ""), name)
-end
-
-function Request.getenv(self, name)
-       if name then
-               return self.message.env[name]
-       else
-               return self.message.env
-       end
-end
-
-function Request.setfilehandler(self, callback)
-       self.filehandler = callback
-
-       if not self.parsed_input then
-               return
-       end
-
-       -- If input has already been parsed then uploads are stored as unlinked
-       -- temporary files pointed to by open file handles in the parameter
-       -- value table. Loop all params, and invoke the file callback for any
-       -- param with an open file handle.
-       local name, value
-       for name, value in pairs(self.message.params) do
-               if type(value) == "table" then
-                       while value.fd do
-                               local data = value.fd:read(1024)
-                               local eof = (not data or data == "")
-
-                               callback(value, data, eof)
-
-                               if eof then
-                                       value.fd:close()
-                                       value.fd = nil
-                               end
-                       end
-               end
-       end
-end
-
-function Request._parse_input(self)
-       parse_message_body(
-                self.input,
-                self.message,
-                self.filehandler
-       )
-       self.parsed_input = true
-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
+       L.http:close()
 end
 
 function content()
-       return context.request:content()
+       return L.http:content()
 end
 
 function formvalue(name, noparse)
-       return context.request:formvalue(name, noparse)
+       return L.http:formvalue(name, noparse)
 end
 
 function formvaluetable(prefix)
-       return context.request:formvaluetable(prefix)
+       return L.http:formvaluetable(prefix)
 end
 
 function getcookie(name)
-       return context.request:getcookie(name)
+       return L.http:getcookie(name)
 end
 
 -- or the environment table itself.
 function getenv(name)
-       return context.request:getenv(name)
+       return L.http:getenv(name)
 end
 
 function setfilehandler(callback)
-       return context.request:setfilehandler(callback)
+       return L.http:setfilehandler(callback)
 end
 
 function header(key, value)
-       if not context.headers then
-               context.headers = {}
-       end
-       context.headers[key:lower()] = value
-       coroutine.yield(2, key, value)
+       L.http:header(key, value)
 end
 
 function prepare_content(mime)
-       if not context.headers or not context.headers["content-type"] then
-               if mime == "application/xhtml+xml" then
-                       if not getenv("HTTP_ACCEPT") or
-                         not getenv("HTTP_ACCEPT"):find("application/xhtml+xml", nil, true) then
-                               mime = "text/html; charset=UTF-8"
-                       end
-                       header("Vary", "Accept")
-               end
-               header("Content-Type", mime)
-       end
+       L.http:prepare_content(mime)
 end
 
 function source()
-       return context.request.input
+       return L.http.input
 end
 
 function status(code, message)
-       code = code or 200
-       message = message or "OK"
-       context.status = code
-       coroutine.yield(1, code, message)
+       L.http:status(code, message)
 end
 
 -- This function is as a valid LTN12 sink.
 -- If the content chunk is nil this function will automatically invoke close.
 function write(content, src_err)
-       if not content then
-               if src_err then
-                       error(src_err)
-               else
-                       close()
-               end
-               return true
-       elseif #content == 0 then
-               return true
-       else
-               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
-                       if not context.headers["cache-control"] then
-                               header("Cache-Control", "no-cache")
-                               header("Expires", "0")
-                       end
-                       if not context.headers["x-frame-options"] then
-                               header("X-Frame-Options", "SAMEORIGIN")
-                       end
-                       if not context.headers["x-xss-protection"] then
-                               header("X-XSS-Protection", "1; mode=block")
-                       end
-                       if not context.headers["x-content-type-options"] then
-                               header("X-Content-Type-Options", "nosniff")
-                       end
-
-                       context.eoh = true
-                       coroutine.yield(3)
-               end
-               coroutine.yield(4, content)
-               return true
+       if src_err then
+               error(src_err)
        end
+
+       return L.print(content)
 end
 
 function splice(fd, size)
@@ -241,10 +73,7 @@ function splice(fd, size)
 end
 
 function redirect(url)
-       if url == "" then url = "/" end
-       status(302, "Found")
-       header("Location", url)
-       close()
+       L.http:redirect(url)
 end
 
 function build_querystring(q)
@@ -266,35 +95,7 @@ urldecode = util.urldecode
 urlencode = util.urlencode
 
 function write_json(x)
-       util.serialize_json(x, write)
-end
-
--- from given url or string. Returns a table with urldecoded values.
--- Simple parameters are stored as string values associated with the parameter
--- name within the table. Parameters with multiple values are stored as array
--- containing the corresponding values.
-function urldecode_params(url, tbl)
-       local parser, name
-       local params = tbl or { }
-
-       parser = lhttp.urlencoded_parser(function (what, buffer, length)
-               if what == parser.TUPLE then
-                       name, value = nil, nil
-               elseif what == parser.NAME then
-                       name = lhttp.urldecode(buffer)
-               elseif what == parser.VALUE and name then
-                       params[name] = lhttp.urldecode(buffer) or ""
-               end
-
-               return true
-       end)
-
-       if parser then
-               parser:parse((url or ""):match("[^?]*$"))
-               parser:parse(nil)
-       end
-
-       return params
+       L.printf('%J', x)
 end
 
 -- separated by "&". Tables are encoded as parameters with multiple values by
@@ -332,223 +133,12 @@ function urlencode_params(tbl)
        return table.concat(enc, "")
 end
 
--- Content-Type. Stores all extracted data associated with its parameter name
--- in the params table within the given message object. Multiple parameter
--- values are stored as tables, ordinary ones as strings.
--- If an optional file callback function is given then it is fed with the
--- file contents chunk by chunk and only the extracted file name is stored
--- within the params table. The callback function will be called subsequently
--- with three arguments:
---  o Table containing decoded (name, file) and raw (headers) mime header data
---  o String value containing a chunk of the file data
---  o Boolean which indicates whether the current chunk is the last one (eof)
-function mimedecode_message_body(src, msg, file_cb)
-       local parser, header, field
-       local len, maxlen = 0, tonumber(msg.env.CONTENT_LENGTH or nil)
-
-       parser, err = lhttp.multipart_parser(msg.env.CONTENT_TYPE, function (what, buffer, length)
-               if what == parser.PART_INIT then
-                       field = { }
-
-               elseif what == parser.HEADER_NAME then
-                       header = buffer:lower()
-
-               elseif what == parser.HEADER_VALUE and header then
-                       if header:lower() == "content-disposition" and
-                          lhttp.header_attribute(buffer, nil) == "form-data"
-                       then
-                               field.name = lhttp.header_attribute(buffer, "name")
-                               field.file = lhttp.header_attribute(buffer, "filename")
-                               field[1] = field.file
-                       end
-
-                       if field.headers then
-                               field.headers[header] = buffer
-                       else
-                               field.headers = { [header] = buffer }
-                       end
-
-               elseif what == parser.PART_BEGIN then
-                       return not field.file
-
-               elseif what == parser.PART_DATA and field.name and length > 0 then
-                       if field.file then
-                               if file_cb then
-                                       file_cb(field, buffer, false)
-                                       msg.params[field.name] = msg.params[field.name] or field
-                               else
-                                       if not field.fd then
-                                               field.fd = nixio.mkstemp(field.name)
-                                       end
-
-                                       if field.fd then
-                                               field.fd:write(buffer)
-                                               msg.params[field.name] = msg.params[field.name] or field
-                                       end
-                               end
-                       else
-                               field.value = buffer
-                       end
-
-               elseif what == parser.PART_END and field.name then
-                       if field.file and msg.params[field.name] then
-                               if file_cb then
-                                       file_cb(field, "", true)
-                               elseif field.fd then
-                                       field.fd:seek(0, "set")
-                               end
-                       else
-                               local val = msg.params[field.name]
-
-                               if type(val) == "table" then
-                                       val[#val+1] = field.value or ""
-                               elseif val ~= nil then
-                                       msg.params[field.name] = { val, field.value or "" }
-                               else
-                                       msg.params[field.name] = field.value or ""
-                               end
-                       end
-
-                       field = nil
-
-               elseif what == parser.ERROR then
-                       err = buffer
-               end
-
-               return true
-       end, HTTP_MAX_CONTENT)
-
-       return ltn12.pump.all(src, function (chunk)
-               len = len + (chunk and #chunk or 0)
-
-               if maxlen and len > maxlen + 2 then
-                       return nil, "Message body size exceeds Content-Length"
-               end
-
-               if not parser or not parser:parse(chunk) then
-                       return nil, err
-               end
-
-               return true
-       end)
-end
-
--- Content-Type. Stores all extracted data associated with its parameter name
--- in the params table within the given message object. Multiple parameter
--- values are stored as tables, ordinary ones as strings.
-function urldecode_message_body(src, msg)
-       local err, name, value, parser
-       local len, maxlen = 0, tonumber(msg.env.CONTENT_LENGTH or nil)
-
-       parser = lhttp.urlencoded_parser(function (what, buffer, length)
-               if what == parser.TUPLE then
-                       name, value = nil, nil
-               elseif what == parser.NAME then
-                       name = lhttp.urldecode(buffer, lhttp.DECODE_PLUS)
-               elseif what == parser.VALUE and name then
-                       local val = msg.params[name]
-
-                       if type(val) == "table" then
-                               val[#val+1] = lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or ""
-                       elseif val ~= nil then
-                               msg.params[name] = { val, lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or "" }
-                       else
-                               msg.params[name] = lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or ""
-                       end
-               elseif what == parser.ERROR then
-                       err = buffer
-               end
-
-               return true
-       end, HTTP_MAX_CONTENT)
-
-       return ltn12.pump.all(src, function (chunk)
-               len = len + (chunk and #chunk or 0)
-
-               if maxlen and len > maxlen + 2 then
-                       return nil, "Message body size exceeds Content-Length"
-               elseif len > HTTP_MAX_CONTENT then
-                       return nil, "Message body size exceeds maximum allowed length"
-               end
-
-               if not parser or not parser:parse(chunk) then
-                       return nil, err
-               end
-
-               return true
-       end)
-end
-
--- This function will examine the Content-Type within the given message object
--- to select the appropriate content decoder.
--- Currently the application/x-www-urlencoded and application/form-data
--- mime types are supported. If the encountered content encoding can't be
--- handled then the whole message body will be stored unaltered as "content"
--- property within the given message object.
-function parse_message_body(src, msg, filecb)
-       if msg.env.CONTENT_LENGTH or msg.env.REQUEST_METHOD == "POST" then
-               local ctype = lhttp.header_attribute(msg.env.CONTENT_TYPE, nil)
-
-               -- Is it multipart/mime ?
-               if ctype == "multipart/form-data" then
-                       return mimedecode_message_body(src, msg, filecb)
-
-               -- Is it application/x-www-form-urlencoded ?
-               elseif ctype == "application/x-www-form-urlencoded" then
-                       return urldecode_message_body(src, msg)
-
-               end
-
-               -- Unhandled encoding
-               -- If a file callback is given then feed it chunk by chunk, else
-               -- store whole buffer in message.content
-               local sink
-
-               -- If we have a file callback then feed it
-               if type(filecb) == "function" then
-                       local meta = {
-                               name = "raw",
-                               encoding = msg.env.CONTENT_TYPE
-                       }
-                       sink = function( chunk )
-                               if chunk then
-                                       return filecb(meta, chunk, false)
-                               else
-                                       return filecb(meta, nil, true)
-                               end
-                       end
-               -- ... else append to .content
-               else
-                       msg.content = ""
-                       msg.content_length = 0
-
-                       sink = function( chunk )
-                               if chunk then
-                                       if ( msg.content_length + #chunk ) <= HTTP_MAX_CONTENT then
-                                               msg.content        = msg.content        .. chunk
-                                               msg.content_length = msg.content_length + #chunk
-                                               return true
-                                       else
-                                               return nil, "POST data exceeds maximum allowed length"
-                                       end
-                               end
-                               return true
-                       end
-               end
-
-               -- Pump data...
-               while true do
-                       local ok, err = ltn12.pump.step( src, sink )
-
-                       if not ok and err then
-                               return nil, err
-                       elseif not ok then -- eof
-                               return true
-                       end
-               end
-
-               return true
-       end
-
-       return false
-end
+context = {
+       request = {
+               formvalue      = function(self, ...) return formvalue(...)      end;
+               formvaluetable = function(self, ...) return formvaluetable(...) end;
+               content        = function(self, ...) return content(...)        end;
+               getcookie      = function(self, ...) return getcookie(...)      end;
+               setfilehandler = function(self, ...) return setfilehandler(...) end;
+       }
+}
index 89757917ff65c22f9210d219f8b377426dfc78de..80013179aad9f101be99fe2ab04853182d73df3c 100644 (file)
@@ -100,32 +100,8 @@ end
 -- Scope manipulation routines
 --
 
-coxpt = setmetatable({}, { __mode = "kv" })
-
-local tl_meta = {
-       __mode = "k",
-
-       __index = function(self, key)
-               local t = rawget(self, coxpt[coroutine.running()]
-                or coroutine.running() or 0)
-               return t and t[key]
-       end,
-
-       __newindex = function(self, key, value)
-               local c = coxpt[coroutine.running()] or coroutine.running() or 0
-               local r = rawget(self, c)
-               if not r then
-                       rawset(self, c, { [key] = value })
-               else
-                       r[key] = value
-               end
-       end
-}
-
--- the current active coroutine. A thread local store is private a table object
--- whose values can't be accessed from outside of the running coroutine.
 function threadlocal(tbl)
-       return setmetatable(tbl or {}, tl_meta)
+       return tbl or {}
 end
 
 
@@ -772,7 +748,6 @@ function coxpcall(f, err, ...)
                        co = coroutine.create(newf)
                end
                coromap[co] = current
-               coxpt[co] = coxpt[current] or current or 0
                return performResume(err, co, ...)
        end
 end
diff --git a/modules/luci-base-ucode/Makefile b/modules/luci-base-ucode/Makefile
deleted file mode 100644 (file)
index 2d5eb84..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# Copyright (C) 2022 Jo-Philipp Wich <jo@mein.io>
-#
-# This is free software, licensed under the Apache License, Version 2.0 .
-#
-
-include $(TOPDIR)/rules.mk
-
-PKG_NAME:=luci-base-ucode
-
-LUCI_TYPE:=mod
-LUCI_BASENAME:=base-ucode
-
-LUCI_TITLE:=LuCI core ucode runtime
-LUCI_DEPENDS:=\
-       +luci-base \
-       +ucode \
-       +ucode-mod-fs \
-       +ucode-mod-uci \
-       +ucode-mod-ubus \
-       +ucode-mod-math \
-       +ucode-mod-lua \
-       +ucode-mod-html \
-       +rpcd-mod-ucode \
-       +liblucihttp-ucode
-
-PKG_LICENSE:=MIT
-
-define Package/luci-base-ucode/postinst
-#!/bin/sh
-
-if [ -z "$${PKG_INSTROOT}" ] && [ -f /etc/config/uhttpd ]; then
-       if ! uci -q get uhttpd.main.ucode_prefix | grep -sq /cgi-bin/luci-ucode; then
-               uci add_list uhttpd.main.ucode_prefix='/cgi-bin/luci-ucode=/usr/share/ucode/luci/uhttpd.uc'
-               uci commit uhttpd
-               service uhttpd reload
-       fi
-fi
-
-exit 0
-endef
-
-include ../../luci.mk
-
-# call BuildPackage - OpenWrt buildroot signature
diff --git a/modules/luci-base-ucode/htdocs/cgi-bin/luci-ucode b/modules/luci-base-ucode/htdocs/cgi-bin/luci-ucode
deleted file mode 100755 (executable)
index 442e427..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env ucode
-
-'use strict';
-
-import { stdin, stdout } from 'fs';
-
-import dispatch from 'luci.dispatcher';
-import request from 'luci.http';
-
-const input_bufsize = 4096;
-let input_available = +getenv('CONTENT_LENGTH') || 0;
-
-function read(len) {
-       if (input_available == 0) {
-               stdin.close();
-
-               return null;
-       }
-
-       let chunk = stdin.read(min(input_available, len ?? input_bufsize, input_bufsize));
-
-       if (chunk == null) {
-               input_available = 0;
-               stdin.close();
-       }
-       else {
-               input_available -= length(chunk);
-       }
-
-       return chunk;
-}
-
-function write(data) {
-       return stdout.write(data);
-}
-
-let req = request(getenv(), read, write);
-
-dispatch(req);
-
-req.close();
diff --git a/modules/luci-base-ucode/luasrc/ucodebridge/luci/http.lua b/modules/luci-base-ucode/luasrc/ucodebridge/luci/http.lua
deleted file mode 100644 (file)
index 06547ae..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
--- Copyright 2008 Steven Barth <steven@midlink.org>
--- Copyright 2010-2018 Jo-Philipp Wich <jo@mein.io>
--- Licensed to the public under the Apache License 2.0.
-
-local util  = require "luci.util"
-local coroutine = require "coroutine"
-local table = require "table"
-local lhttp = require "lucihttp"
-
-local L, table, ipairs, pairs, type, error = _G.L, table, ipairs, pairs, type, error
-
-module "luci.http"
-
-HTTP_MAX_CONTENT      = 1024*100               -- 100 kB maximum content size
-
-function close()
-       L.http:close()
-end
-
-function content()
-       return L.http:content()
-end
-
-function formvalue(name, noparse)
-       return L.http:formvalue(name, noparse)
-end
-
-function formvaluetable(prefix)
-       return L.http:formvaluetable(prefix)
-end
-
-function getcookie(name)
-       return L.http:getcookie(name)
-end
-
--- or the environment table itself.
-function getenv(name)
-       return L.http:getenv(name)
-end
-
-function setfilehandler(callback)
-       return L.http:setfilehandler(callback)
-end
-
-function header(key, value)
-       L.http:header(key, value)
-end
-
-function prepare_content(mime)
-       L.http:prepare_content(mime)
-end
-
-function source()
-       return L.http.input
-end
-
-function status(code, message)
-       L.http:status(code, message)
-end
-
--- This function is as a valid LTN12 sink.
--- If the content chunk is nil this function will automatically invoke close.
-function write(content, src_err)
-       if src_err then
-               error(src_err)
-       end
-
-       return L.print(content)
-end
-
-function splice(fd, size)
-       coroutine.yield(6, fd, size)
-end
-
-function redirect(url)
-       L.http:redirect(url)
-end
-
-function build_querystring(q)
-       local s, n, k, v = {}, 1, nil, nil
-
-       for k, v in pairs(q) do
-               s[n+0] = (n == 1) and "?" or "&"
-               s[n+1] = util.urlencode(k)
-               s[n+2] = "="
-               s[n+3] = util.urlencode(v)
-               n = n + 4
-       end
-
-       return table.concat(s, "")
-end
-
-urldecode = util.urldecode
-
-urlencode = util.urlencode
-
-function write_json(x)
-       L.printf('%J', x)
-end
-
--- separated by "&". Tables are encoded as parameters with multiple values by
--- repeating the parameter name with each value.
-function urlencode_params(tbl)
-       local k, v
-       local n, enc = 1, {}
-       for k, v in pairs(tbl) do
-               if type(v) == "table" then
-                       local i, v2
-                       for i, v2 in ipairs(v) do
-                               if enc[1] then
-                                       enc[n] = "&"
-                                       n = n + 1
-                               end
-
-                               enc[n+0] = lhttp.urlencode(k)
-                               enc[n+1] = "="
-                               enc[n+2] = lhttp.urlencode(v2)
-                               n = n + 3
-                       end
-               else
-                       if enc[1] then
-                               enc[n] = "&"
-                               n = n + 1
-                       end
-
-                       enc[n+0] = lhttp.urlencode(k)
-                       enc[n+1] = "="
-                       enc[n+2] = lhttp.urlencode(v)
-                       n = n + 3
-               end
-       end
-
-       return table.concat(enc, "")
-end
-
-context = {
-       request = {
-               formvalue      = function(self, ...) return formvalue(...)      end;
-               formvaluetable = function(self, ...) return formvaluetable(...) end;
-               content        = function(self, ...) return content(...)        end;
-               getcookie      = function(self, ...) return getcookie(...)      end;
-               setfilehandler = function(self, ...) return setfilehandler(...) end;
-       }
-}
diff --git a/modules/luci-base-ucode/luasrc/ucodebridge/luci/util.lua b/modules/luci-base-ucode/luasrc/ucodebridge/luci/util.lua
deleted file mode 100644 (file)
index d50eb1d..0000000
+++ /dev/null
@@ -1,786 +0,0 @@
--- Copyright 2008 Steven Barth <steven@midlink.org>
--- Licensed to the public under the Apache License 2.0.
-
-local io = require "io"
-local math = require "math"
-local table = require "table"
-local debug = require "debug"
-local ldebug = require "luci.debug"
-local string = require "string"
-local coroutine = require "coroutine"
-local tparser = require "luci.template.parser"
-local json = require "luci.jsonc"
-local lhttp = require "lucihttp"
-
-local _ubus = require "ubus"
-local _ubus_connection = nil
-
-local getmetatable, setmetatable = getmetatable, setmetatable
-local rawget, rawset, unpack, select = rawget, rawset, unpack, select
-local tostring, type, assert, error = tostring, type, assert, error
-local ipairs, pairs, next, loadstring = ipairs, pairs, next, loadstring
-local require, pcall, xpcall = require, pcall, xpcall
-local collectgarbage, get_memory_limit = collectgarbage, get_memory_limit
-
-local L = _G.L
-
-module "luci.util"
-
---
--- Pythonic string formatting extension
---
-getmetatable("").__mod = function(a, b)
-       local ok, res
-
-       if not b then
-               return a
-       elseif type(b) == "table" then
-               local k, _
-               for k, _ in pairs(b) do if type(b[k]) == "userdata" then b[k] = tostring(b[k]) end end
-
-               ok, res = pcall(a.format, a, unpack(b))
-               if not ok then
-                       error(res, 2)
-               end
-               return res
-       else
-               if type(b) == "userdata" then b = tostring(b) end
-
-               ok, res = pcall(a.format, a, b)
-               if not ok then
-                       error(res, 2)
-               end
-               return res
-       end
-end
-
-
---
--- Class helper routines
---
-
--- Instantiates a class
-local function _instantiate(class, ...)
-       local inst = setmetatable({}, {__index = class})
-
-       if inst.__init__ then
-               inst:__init__(...)
-       end
-
-       return inst
-end
-
--- The class object can be instantiated by calling itself.
--- Any class functions or shared parameters can be attached to this object.
--- Attaching a table to the class object makes this table shared between
--- all instances of this class. For object parameters use the __init__ function.
--- Classes can inherit member functions and values from a base class.
--- Class can be instantiated by calling them. All parameters will be passed
--- to the __init__ function of this class - if such a function exists.
--- The __init__ function must be used to set any object parameters that are not shared
--- with other objects of this class. Any return values will be ignored.
-function class(base)
-       return setmetatable({}, {
-               __call  = _instantiate,
-               __index = base
-       })
-end
-
-function instanceof(object, class)
-       local meta = getmetatable(object)
-       while meta and meta.__index do
-               if meta.__index == class then
-                       return true
-               end
-               meta = getmetatable(meta.__index)
-       end
-       return false
-end
-
-
---
--- Scope manipulation routines
---
-
-coxpt = setmetatable({}, { __mode = "kv" })
-
-local tl_meta = {
-       __mode = "k",
-
-       __index = function(self, key)
-               local t = rawget(self, coxpt[coroutine.running()]
-                or coroutine.running() or 0)
-               L.http:write("<!-- __index(%s/%s, %s): %s -->\n" %{ tostring(self), tostring(coxpt[coroutine.running()] or coroutine.running() or 0), key, tostring(t and t[key]) })
-               return t and t[key]
-       end,
-
-       __newindex = function(self, key, value)
-               L.http:write("<!-- __newindex(%s/%s, %s, %s) -->\n" %{ tostring(self), tostring(coxpt[coroutine.running()] or coroutine.running() or 0), key, tostring(value) })
-               local c = coxpt[coroutine.running()] or coroutine.running() or 0
-               local r = rawget(self, c)
-               if not r then
-                       rawset(self, c, { [key] = value })
-               else
-                       r[key] = value
-               end
-       end
-}
-
--- the current active coroutine. A thread local store is private a table object
--- whose values can't be accessed from outside of the running coroutine.
-function threadlocal(tbl)
-       return tbl or {} --setmetatable(tbl or {}, tl_meta)
-end
-
-
---
--- Debugging routines
---
-
-function perror(obj)
-       return io.stderr:write(tostring(obj) .. "\n")
-end
-
-function dumptable(t, maxdepth, i, seen)
-       i = i or 0
-       seen = seen or setmetatable({}, {__mode="k"})
-
-       for k,v in pairs(t) do
-               perror(string.rep("\t", i) .. tostring(k) .. "\t" .. tostring(v))
-               if type(v) == "table" and (not maxdepth or i < maxdepth) then
-                       if not seen[v] then
-                               seen[v] = true
-                               dumptable(v, maxdepth, i+1, seen)
-                       else
-                               perror(string.rep("\t", i) .. "*** RECURSION ***")
-                       end
-               end
-       end
-end
-
-
---
--- String and data manipulation routines
---
-
--- compatibility wrapper for xml.pcdata
-function pcdata(value)
-       local xml = require "luci.xml"
-
-       perror("luci.util.pcdata() has been replaced by luci.xml.pcdata() - Please update your code.")
-       return xml.pcdata(value)
-end
-
-function urlencode(value)
-       if value ~= nil then
-               local str = tostring(value)
-               return lhttp.urlencode(str, lhttp.ENCODE_IF_NEEDED + lhttp.ENCODE_FULL)
-                       or str
-       end
-       return nil
-end
-
-function urldecode(value, decode_plus)
-       if value ~= nil then
-               local flag = decode_plus and lhttp.DECODE_PLUS or 0
-               local str = tostring(value)
-               return lhttp.urldecode(str, lhttp.DECODE_IF_NEEDED + flag)
-                       or str
-       end
-       return nil
-end
-
--- compatibility wrapper for xml.striptags
-function striptags(value)
-       local xml = require "luci.xml"
-
-       perror("luci.util.striptags() has been replaced by luci.xml.striptags() - Please update your code.")
-       return xml.striptags(value)
-end
-
-function shellquote(value)
-       return string.format("'%s'", string.gsub(value or "", "'", "'\\''"))
-end
-
--- for bash, ash and similar shells single-quoted strings are taken
--- literally except for single quotes (which terminate the string)
--- (and the exception noted below for dash (-) at the start of a
--- command line parameter).
-function shellsqescape(value)
-   local res
-   res, _ = string.gsub(value, "'", "'\\''")
-   return res
-end
-
--- bash, ash and other similar shells interpret a dash (-) at the start
--- of a command-line parameters as an option indicator regardless of
--- whether it is inside a single-quoted string.  It must be backlash
--- escaped to resolve this.  This requires in some funky special-case
--- handling.  It may actually be a property of the getopt function
--- rather than the shell proper.
-function shellstartsqescape(value)
-   res, _ = string.gsub(value, "^%-", "\\-")
-   return shellsqescape(res)
-end
-
--- containing the resulting substrings. The optional max parameter specifies
--- the number of bytes to process, regardless of the actual length of the given
--- string. The optional last parameter, regex, specifies whether the separator
--- sequence is interpreted as regular expression.
---                                     pattern as regular expression (optional, default is false)
-function split(str, pat, max, regex)
-       pat = pat or "\n"
-       max = max or #str
-
-       local t = {}
-       local c = 1
-
-       if #str == 0 then
-               return {""}
-       end
-
-       if #pat == 0 then
-               return nil
-       end
-
-       if max == 0 then
-               return str
-       end
-
-       repeat
-               local s, e = str:find(pat, c, not regex)
-               max = max - 1
-               if s and max < 0 then
-                       t[#t+1] = str:sub(c)
-               else
-                       t[#t+1] = str:sub(c, s and s - 1)
-               end
-               c = e and e + 1 or #str + 1
-       until not s or max < 0
-
-       return t
-end
-
-function trim(str)
-       return (str:gsub("^%s*(.-)%s*$", "%1"))
-end
-
-function cmatch(str, pat)
-       local count = 0
-       for _ in str:gmatch(pat) do count = count + 1 end
-       return count
-end
-
--- one token per invocation, the tokens are separated by whitespace. If the
--- input value is a table, it is transformed into a string first. A nil value
--- will result in a valid iterator which aborts with the first invocation.
-function imatch(v)
-       if type(v) == "table" then
-               local k = nil
-               return function()
-                       k = next(v, k)
-                       return v[k]
-               end
-
-       elseif type(v) == "number" or type(v) == "boolean" then
-               local x = true
-               return function()
-                       if x then
-                               x = false
-                               return tostring(v)
-                       end
-               end
-
-       elseif type(v) == "userdata" or type(v) == "string" then
-               return tostring(v):gmatch("%S+")
-       end
-
-       return function() end
-end
-
--- value or 0 if the unit is unknown. Upper- or lower case is irrelevant.
--- Recognized units are:
---     o "y"   - one year   (60*60*24*366)
---  o "m"      - one month  (60*60*24*31)
---  o "w"      - one week   (60*60*24*7)
---  o "d"      - one day    (60*60*24)
---  o "h"      - one hour       (60*60)
---  o "min"    - one minute (60)
---  o "kb"  - one kilobyte (1024)
---  o "mb"     - one megabyte (1024*1024)
---  o "gb"     - one gigabyte (1024*1024*1024)
---  o "kib" - one si kilobyte (1000)
---  o "mib"    - one si megabyte (1000*1000)
---  o "gib"    - one si gigabyte (1000*1000*1000)
-function parse_units(ustr)
-
-       local val = 0
-
-       -- unit map
-       local map = {
-               -- date stuff
-               y   = 60 * 60 * 24 * 366,
-               m   = 60 * 60 * 24 * 31,
-               w   = 60 * 60 * 24 * 7,
-               d   = 60 * 60 * 24,
-               h   = 60 * 60,
-               min = 60,
-
-               -- storage sizes
-               kb  = 1024,
-               mb  = 1024 * 1024,
-               gb  = 1024 * 1024 * 1024,
-
-               -- storage sizes (si)
-               kib = 1000,
-               mib = 1000 * 1000,
-               gib = 1000 * 1000 * 1000
-       }
-
-       -- parse input string
-       for spec in ustr:lower():gmatch("[0-9%.]+[a-zA-Z]*") do
-
-               local num = spec:gsub("[^0-9%.]+$","")
-               local spn = spec:gsub("^[0-9%.]+", "")
-
-               if map[spn] or map[spn:sub(1,1)] then
-                       val = val + num * ( map[spn] or map[spn:sub(1,1)] )
-               else
-                       val = val + num
-               end
-       end
-
-
-       return val
-end
-
--- also register functions above in the central string class for convenience
-string.split       = split
-string.trim        = trim
-string.cmatch      = cmatch
-string.parse_units = parse_units
-
-
-function append(src, ...)
-       for i, a in ipairs({...}) do
-               if type(a) == "table" then
-                       for j, v in ipairs(a) do
-                               src[#src+1] = v
-                       end
-               else
-                       src[#src+1] = a
-               end
-       end
-       return src
-end
-
-function combine(...)
-       return append({}, ...)
-end
-
-function contains(table, value)
-       for k, v in pairs(table) do
-               if value == v then
-                       return k
-               end
-       end
-       return false
-end
-
--- Both table are - in fact - merged together.
-function update(t, updates)
-       for k, v in pairs(updates) do
-               t[k] = v
-       end
-end
-
-function keys(t)
-       local keys = { }
-       if t then
-               for k, _ in kspairs(t) do
-                       keys[#keys+1] = k
-               end
-       end
-       return keys
-end
-
-function clone(object, deep)
-       local copy = {}
-
-       for k, v in pairs(object) do
-               if deep and type(v) == "table" then
-                       v = clone(v, deep)
-               end
-               copy[k] = v
-       end
-
-       return setmetatable(copy, getmetatable(object))
-end
-
-
--- Serialize the contents of a table value.
-function _serialize_table(t, seen)
-       assert(not seen[t], "Recursion detected.")
-       seen[t] = true
-
-       local data  = ""
-       local idata = ""
-       local ilen  = 0
-
-       for k, v in pairs(t) do
-               if type(k) ~= "number" or k < 1 or math.floor(k) ~= k or ( k - #t ) > 3 then
-                       k = serialize_data(k, seen)
-                       v = serialize_data(v, seen)
-                       data = data .. ( #data > 0 and ", " or "" ) ..
-                               '[' .. k .. '] = ' .. v
-               elseif k > ilen then
-                       ilen = k
-               end
-       end
-
-       for i = 1, ilen do
-               local v = serialize_data(t[i], seen)
-               idata = idata .. ( #idata > 0 and ", " or "" ) .. v
-       end
-
-       return idata .. ( #data > 0 and #idata > 0 and ", " or "" ) .. data
-end
-
--- with loadstring().
-function serialize_data(val, seen)
-       seen = seen or setmetatable({}, {__mode="k"})
-
-       if val == nil then
-               return "nil"
-       elseif type(val) == "number" then
-               return val
-       elseif type(val) == "string" then
-               return "%q" % val
-       elseif type(val) == "boolean" then
-               return val and "true" or "false"
-       elseif type(val) == "function" then
-               return "loadstring(%q)" % get_bytecode(val)
-       elseif type(val) == "table" then
-               return "{ " .. _serialize_table(val, seen) .. " }"
-       else
-               return '"[unhandled data type:' .. type(val) .. ']"'
-       end
-end
-
-function restore_data(str)
-       return loadstring("return " .. str)()
-end
-
-
---
--- Byte code manipulation routines
---
-
--- will be stripped before it is returned.
-function get_bytecode(val)
-       local code
-
-       if type(val) == "function" then
-               code = string.dump(val)
-       else
-               code = string.dump( loadstring( "return " .. serialize_data(val) ) )
-       end
-
-       return code -- and strip_bytecode(code)
-end
-
--- numbers and debugging numbers will be discarded. Original version by
--- Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
-function strip_bytecode(code)
-       local version, format, endian, int, size, ins, num, lnum = code:byte(5, 12)
-       local subint
-       if endian == 1 then
-               subint = function(code, i, l)
-                       local val = 0
-                       for n = l, 1, -1 do
-                               val = val * 256 + code:byte(i + n - 1)
-                       end
-                       return val, i + l
-               end
-       else
-               subint = function(code, i, l)
-                       local val = 0
-                       for n = 1, l, 1 do
-                               val = val * 256 + code:byte(i + n - 1)
-                       end
-                       return val, i + l
-               end
-       end
-
-       local function strip_function(code)
-               local count, offset = subint(code, 1, size)
-               local stripped = { string.rep("\0", size) }
-               local dirty = offset + count
-               offset = offset + count + int * 2 + 4
-               offset = offset + int + subint(code, offset, int) * ins
-               count, offset = subint(code, offset, int)
-               for n = 1, count do
-                       local t
-                       t, offset = subint(code, offset, 1)
-                       if t == 1 then
-                               offset = offset + 1
-                       elseif t == 4 then
-                               offset = offset + size + subint(code, offset, size)
-                       elseif t == 3 then
-                               offset = offset + num
-                       elseif t == 254 or t == 9 then
-                               offset = offset + lnum
-                       end
-               end
-               count, offset = subint(code, offset, int)
-               stripped[#stripped+1] = code:sub(dirty, offset - 1)
-               for n = 1, count do
-                       local proto, off = strip_function(code:sub(offset, -1))
-                       stripped[#stripped+1] = proto
-                       offset = offset + off - 1
-               end
-               offset = offset + subint(code, offset, int) * int + int
-               count, offset = subint(code, offset, int)
-               for n = 1, count do
-                       offset = offset + subint(code, offset, size) + size + int * 2
-               end
-               count, offset = subint(code, offset, int)
-               for n = 1, count do
-                       offset = offset + subint(code, offset, size) + size
-               end
-               stripped[#stripped+1] = string.rep("\0", int * 3)
-               return table.concat(stripped), offset
-       end
-
-       return code:sub(1,12) .. strip_function(code:sub(13,-1))
-end
-
-
---
--- Sorting iterator functions
---
-
-function _sortiter( t, f )
-       local keys = { }
-
-       local k, v
-       for k, v in pairs(t) do
-               keys[#keys+1] = k
-       end
-
-       local _pos = 0
-
-       table.sort( keys, f )
-
-       return function()
-               _pos = _pos + 1
-               if _pos <= #keys then
-                       return keys[_pos], t[keys[_pos]], _pos
-               end
-       end
-end
-
--- the provided callback function.
-function spairs(t,f)
-       return _sortiter( t, f )
-end
-
--- The table pairs are sorted by key.
-function kspairs(t)
-       return _sortiter( t )
-end
-
--- The table pairs are sorted by value.
-function vspairs(t)
-       return _sortiter( t, function (a,b) return t[a] < t[b] end )
-end
-
-
---
--- System utility functions
---
-
-function bigendian()
-       return string.byte(string.dump(function() end), 7) == 0
-end
-
-function exec(command)
-       local pp   = io.popen(command)
-       local data = pp:read("*a")
-       pp:close()
-
-       return data
-end
-
-function execi(command)
-       local pp = io.popen(command)
-
-       return pp and function()
-               local line = pp:read()
-
-               if not line then
-                       pp:close()
-               end
-
-               return line
-       end
-end
-
--- Deprecated
-function execl(command)
-       local pp   = io.popen(command)
-       local line = ""
-       local data = {}
-
-       while true do
-               line = pp:read()
-               if (line == nil) then break end
-               data[#data+1] = line
-       end
-       pp:close()
-
-       return data
-end
-
-
-local ubus_codes = {
-       "INVALID_COMMAND",
-       "INVALID_ARGUMENT",
-       "METHOD_NOT_FOUND",
-       "NOT_FOUND",
-       "NO_DATA",
-       "PERMISSION_DENIED",
-       "TIMEOUT",
-       "NOT_SUPPORTED",
-       "UNKNOWN_ERROR",
-       "CONNECTION_FAILED"
-}
-
-local function ubus_return(...)
-       if select('#', ...) == 2 then
-               local rv, err = select(1, ...), select(2, ...)
-               if rv == nil and type(err) == "number" then
-                       return nil, err, ubus_codes[err]
-               end
-       end
-
-       return ...
-end
-
-function ubus(object, method, data, path, timeout)
-       if not _ubus_connection then
-               _ubus_connection = _ubus.connect(path, timeout)
-               assert(_ubus_connection, "Unable to establish ubus connection")
-       end
-
-       if object and method then
-               if type(data) ~= "table" then
-                       data = { }
-               end
-               return ubus_return(_ubus_connection:call(object, method, data))
-       elseif object then
-               return _ubus_connection:signatures(object)
-       else
-               return _ubus_connection:objects()
-       end
-end
-
-function serialize_json(x, cb)
-       local js = json.stringify(x)
-       if type(cb) == "function" then
-               cb(js)
-       else
-               return js
-       end
-end
-
-
-function libpath()
-       return require "nixio.fs".dirname(ldebug.__file__)
-end
-
-function checklib(fullpathexe, wantedlib)
-       local fs = require "nixio.fs"
-       local haveldd = fs.access('/usr/bin/ldd')
-       local haveexe = fs.access(fullpathexe)
-       if not haveldd or not haveexe then
-               return false
-       end
-       local libs = exec(string.format("/usr/bin/ldd %s", shellquote(fullpathexe)))
-       if not libs then
-               return false
-       end
-       for k, v in ipairs(split(libs)) do
-               if v:find(wantedlib) then
-                       return true
-               end
-       end
-       return false
-end
-
--------------------------------------------------------------------------------
--- Coroutine safe xpcall and pcall versions
---
--- Encapsulates the protected calls with a coroutine based loop, so errors can
--- be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines
--- yielding inside the call to pcall or xpcall.
---
--- Authors: Roberto Ierusalimschy and Andre Carregal
--- Contributors: Thomas Harning Jr., Ignacio Burgueño, Fabio Mascarenhas
---
--- Copyright 2005 - Kepler Project
---
--- $Id: coxpcall.lua,v 1.13 2008/05/19 19:20:02 mascarenhas Exp $
--------------------------------------------------------------------------------
-
--------------------------------------------------------------------------------
--- Implements xpcall with coroutines
--------------------------------------------------------------------------------
-local coromap = setmetatable({}, { __mode = "k" })
-
-local 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
-
-local function id(trace, ...)
-       return trace
-end
-
-function coxpcall(f, err, ...)
-       local current = coroutine.running()
-       if not current then
-               if err == id then
-                       return pcall(f, ...)
-               else
-                       if select("#", ...) > 0 then
-                               local oldf, params = f, { ... }
-                               f = function() return oldf(unpack(params)) end
-                       end
-                       return xpcall(f, err)
-               end
-       else
-               local res, co = pcall(coroutine.create, f)
-               if not res then
-                       local newf = function(...) return f(...) end
-                       co = coroutine.create(newf)
-               end
-               coromap[co] = current
-               coxpt[co] = coxpt[current] or current or 0
-               return performResume(err, co, ...)
-       end
-end
-
-function copcall(f, ...)
-       return coxpcall(f, id, ...)
-end
diff --git a/modules/luci-base-ucode/src/Makefile b/modules/luci-base-ucode/src/Makefile
deleted file mode 100644 (file)
index 8aeb29a..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-%.o: %.c
-       $(CC) $(CPPFLAGS) $(CFLAGS) $(FPIC) -DNDEBUG -c -o $@ $<
-
-contrib/lemon: contrib/lemon.c contrib/lempar.c
-       cc -o contrib/lemon $<
-
-lib/plural_formula.c: lib/plural_formula.y contrib/lemon
-       ./contrib/lemon -q $<
-
-lib/lmo.c: lib/plural_formula.c
-
-core.so: lib/luci.o lib/lmo.o lib/plural_formula.o
-       $(CC) $(LDFLAGS) -shared -o $@ $^
-
-version.uc:
-       echo "export const revision = '$(LUCI_VERSION)', branch = '$(LUCI_GITBRANCH)';" > $@
-
-clean:
-       rm -f contrib/lemon lib/*.o lib/plural_formula.c lib/plural_formula.h core.so version.uc
-
-compile: core.so version.uc
-
-install: compile
-       mkdir -p $(DESTDIR)/usr/lib/ucode/luci
-       cp core.so $(DESTDIR)/usr/lib/ucode/luci/core.so
-
-       mkdir -p $(DESTDIR)/usr/share/ucode/luci
-       cp version.uc $(DESTDIR)/usr/share/ucode/luci/version.uc
index 6cb2b640925adbd14d90274da4ff547a265ec7f8..f85f75332028005855189c607ebe706edf67ce05 100644 (file)
@@ -1,5 +1,5 @@
 #
-# Copyright (C) 2008-2015 The LuCI Team <luci@lists.subsignal.org>
+# Copyright (C) 2022 Jo-Philipp Wich <jo@mein.io>
 #
 # This is free software, licensed under the Apache License, Version 2.0 .
 #
@@ -11,8 +11,20 @@ PKG_NAME:=luci-base
 LUCI_TYPE:=mod
 LUCI_BASENAME:=base
 
-LUCI_TITLE:=LuCI core libraries
-LUCI_DEPENDS:=+lua +luci-lib-nixio +luci-lib-ip +rpcd +libubus-lua +luci-lib-jsonc +liblucihttp-lua +luci-lib-base +rpcd-mod-file +rpcd-mod-luci +cgi-io
+LUCI_TITLE:=LuCI core runtime
+LUCI_DEPENDS:=\
+       +rpcd \
+       +rpcd-mod-file \
+       +rpcd-mod-luci \
+       +rpcd-mod-ucode \
+       +cgi-io \
+       +ucode \
+       +ucode-mod-fs \
+       +ucode-mod-uci \
+       +ucode-mod-ubus \
+       +ucode-mod-math \
+       +ucode-mod-html \
+       +liblucihttp-ucode
 
 PKG_LICENSE:=MIT
 
@@ -26,6 +38,20 @@ define Package/luci-base/conffiles
 /etc/config/ucitrack
 endef
 
+define Package/luci-base/postinst
+#!/bin/sh
+
+if [ -z "$${PKG_INSTROOT}" ] && [ -f /etc/config/uhttpd ]; then
+       if ! uci -q get uhttpd.main.ucode_prefix | grep -sq /cgi-bin/luci; then
+               uci add_list uhttpd.main.ucode_prefix='/cgi-bin/luci=/usr/share/ucode/luci/uhttpd.uc'
+               uci commit uhttpd
+               service uhttpd reload
+       fi
+fi
+
+exit 0
+endef
+
 include ../../luci.mk
 
 define Host/Configure
index c5c98473469eba6bd6b21879690026bea0758960..442e427d410f1824b7b07d48bab8a2730012989b 100755 (executable)
@@ -1,5 +1,41 @@
-#!/usr/bin/lua
-require "luci.cacheloader"
-require "luci.sgi.cgi"
-luci.dispatcher.indexcache = "/tmp/luci-indexcache"
-luci.sgi.cgi.run()
+#!/usr/bin/env ucode
+
+'use strict';
+
+import { stdin, stdout } from 'fs';
+
+import dispatch from 'luci.dispatcher';
+import request from 'luci.http';
+
+const input_bufsize = 4096;
+let input_available = +getenv('CONTENT_LENGTH') || 0;
+
+function read(len) {
+       if (input_available == 0) {
+               stdin.close();
+
+               return null;
+       }
+
+       let chunk = stdin.read(min(input_available, len ?? input_bufsize, input_bufsize));
+
+       if (chunk == null) {
+               input_available = 0;
+               stdin.close();
+       }
+       else {
+               input_available -= length(chunk);
+       }
+
+       return chunk;
+}
+
+function write(data) {
+       return stdout.write(data);
+}
+
+let req = request(getenv(), read, write);
+
+dispatch(req);
+
+req.close();
diff --git a/modules/luci-base/luasrc/controller/admin/index.lua b/modules/luci-base/luasrc/controller/admin/index.lua
deleted file mode 100644 (file)
index 8f9b481..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
--- Copyright 2008 Steven Barth <steven@midlink.org>
--- Licensed to the public under the Apache License 2.0.
-
-module("luci.controller.admin.index", package.seeall)
-
-function action_logout()
-       local dsp = require "luci.dispatcher"
-       local utl = require "luci.util"
-       local sid = dsp.context.authsession
-
-       if sid then
-               utl.ubus("session", "destroy", { ubus_rpc_session = sid })
-
-               local url = dsp.build_url()
-
-               if luci.http.getenv('HTTPS') == 'on' then
-                       luci.http.header("Set-Cookie", "sysauth_https=; expires=Thu, 01 Jan 1970 01:00:00 GMT; path=%s" % url)
-               end
-
-               luci.http.header("Set-Cookie", "sysauth_http=; expires=Thu, 01 Jan 1970 01:00:00 GMT; path=%s" % url)
-       end
-
-       luci.http.redirect(dsp.build_url())
-end
-
-function action_translations(lang)
-       local i18n = require "luci.i18n"
-       local http = require "luci.http"
-       local fs = require "nixio".fs
-
-       if lang and #lang > 0 then
-               lang = i18n.setlanguage(lang)
-               if lang then
-                       local s = fs.stat("%s/base.%s.lmo" %{ i18n.i18ndir, lang })
-                       if s then
-                               http.header("Cache-Control", "public, max-age=31536000")
-                               http.header("ETag", "%x-%x-%x" %{ s["ino"], s["size"], s["mtime"] })
-                       end
-               end
-       end
-
-       http.prepare_content("application/javascript; charset=utf-8")
-       http.write("window.TR=")
-       http.write_json(i18n.dump())
-end
-
-local function ubus_reply(id, data, code, errmsg)
-       local reply = { jsonrpc = "2.0", id = id }
-       if errmsg then
-               reply.error = {
-                       code = code,
-                       message = errmsg
-               }
-       elseif type(code) == "table" then
-               reply.result = code
-       else
-               reply.result = { code, data }
-       end
-
-       return reply
-end
-
-local ubus_types = {
-       nil,
-       "array",
-       "object",
-       "string",
-       nil, -- INT64
-       "number",
-       nil, -- INT16,
-       "boolean",
-       "double"
-}
-
-local function ubus_access(sid, obj, fun)
-       local res, code = luci.util.ubus("session", "access", {
-               ubus_rpc_session = sid,
-               scope            = "ubus",
-               object           = obj,
-               ["function"]     = fun
-       })
-
-       return (type(res) == "table" and res.access == true)
-end
-
-local function ubus_request(req)
-       if type(req) ~= "table" or type(req.method) ~= "string" or req.jsonrpc ~= "2.0" or req.id == nil then
-               return ubus_reply(nil, nil, -32600, "Invalid request")
-
-       elseif req.method == "call" then
-               if type(req.params) ~= "table" or #req.params < 3 then
-                       return ubus_reply(nil, nil, -32600, "Invalid parameters")
-               end
-
-               local sid, obj, fun, arg =
-                       req.params[1], req.params[2], req.params[3], req.params[4] or {}
-               if type(arg) ~= "table" or arg.ubus_rpc_session ~= nil then
-                       return ubus_reply(req.id, nil, -32602, "Invalid parameters")
-               end
-
-               if sid == "00000000000000000000000000000000" and luci.dispatcher.context.authsession then
-                       sid = luci.dispatcher.context.authsession
-               end
-
-               if not ubus_access(sid, obj, fun) then
-                       return ubus_reply(req.id, nil, -32002, "Access denied")
-               end
-
-               arg.ubus_rpc_session = sid
-
-               local res, code = luci.util.ubus(obj, fun, arg)
-               return ubus_reply(req.id, res, code or 0)
-
-       elseif req.method == "list" then
-               if req.params == nil or (type(req.params) == "table" and #req.params == 0) then
-                       local objs = luci.util.ubus()
-                       return ubus_reply(req.id, nil, objs)
-
-               elseif type(req.params) == "table" then
-                       local n, rv = nil, {}
-                       for n = 1, #req.params do
-                               if type(req.params[n]) ~= "string" then
-                                       return ubus_reply(req.id, nil, -32602, "Invalid parameters")
-                               end
-
-                               local sig = luci.util.ubus(req.params[n])
-                               if sig and type(sig) == "table" then
-                                       rv[req.params[n]] = {}
-
-                                       local m, p
-                                       for m, p in pairs(sig) do
-                                               if type(p) == "table" then
-                                                       rv[req.params[n]][m] = {}
-
-                                                       local pn, pt
-                                                       for pn, pt in pairs(p) do
-                                                               rv[req.params[n]][m][pn] = ubus_types[pt] or "unknown"
-                                                       end
-                                               end
-                                       end
-                               end
-                       end
-                       return ubus_reply(req.id, nil, rv)
-
-               else
-                       return ubus_reply(req.id, nil, -32602, "Invalid parameters")
-               end
-       end
-
-       return ubus_reply(req.id, nil, -32601, "Method not found")
-end
-
-function action_ubus()
-       local parser = require "luci.jsonc".new()
-
-       luci.http.context.request:setfilehandler(function(_, s)
-               if not s then
-                       return nil
-               end
-
-               local ok, err = parser:parse(s)
-               return (not err or nil)
-       end)
-
-       luci.http.context.request:content()
-
-       local json = parser:get()
-       if json == nil or type(json) ~= "table" then
-               luci.http.prepare_content("application/json")
-               luci.http.write_json(ubus_reply(nil, nil, -32700, "Parse error"))
-               return
-       end
-
-       local response
-       if #json == 0 then
-               response = ubus_request(json)
-       else
-               response = {}
-
-               local _, request
-               for _, request in ipairs(json) do
-                       response[_] = ubus_request(request)
-               end
-       end
-
-       luci.http.prepare_content("application/json")
-       luci.http.write_json(response)
-end
-
-function action_menu()
-       local dsp = require "luci.dispatcher"
-       local http = require "luci.http"
-
-       local _, _, acls = dsp.is_authenticated({ methods = { "cookie:sysauth_https", "cookie:sysauth_http" } })
-       local menu = dsp.menu_json(acls or {}) or {}
-
-       http.prepare_content("application/json")
-       http.write_json(menu)
-end
diff --git a/modules/luci-base/luasrc/controller/admin/uci.lua b/modules/luci-base/luasrc/controller/admin/uci.lua
deleted file mode 100644 (file)
index 7aad10d..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
--- Copyright 2008 Steven Barth <steven@midlink.org>
--- Copyright 2010-2019 Jo-Philipp Wich <jo@mein.io>
--- Licensed to the public under the Apache License 2.0.
-
-module("luci.controller.admin.uci", package.seeall)
-
-local function ubus_state_to_http(errstr)
-       local map = {
-               ["Invalid command"]   = 400,
-               ["Invalid argument"]  = 400,
-               ["Method not found"]  = 404,
-               ["Entry not found"]   = 404,
-               ["No data"]           = 204,
-               ["Permission denied"] = 403,
-               ["Timeout"]           = 504,
-               ["Not supported"]     = 500,
-               ["Unknown error"]     = 500,
-               ["Connection failed"] = 503
-       }
-
-       local code = map[errstr] or 200
-       local msg  = errstr      or "OK"
-
-       luci.http.status(code, msg)
-
-       if code ~= 204 then
-               luci.http.prepare_content("text/plain")
-               luci.http.write(msg)
-       end
-end
-
-function action_apply_rollback()
-       local uci = require "luci.model.uci"
-       local token, errstr = uci:apply(true)
-       if token then
-               luci.http.prepare_content("application/json")
-               luci.http.write_json({ token = token })
-       else
-               ubus_state_to_http(errstr)
-       end
-end
-
-function action_apply_unchecked()
-       local uci = require "luci.model.uci"
-       local _, errstr = uci:apply(false)
-       ubus_state_to_http(errstr)
-end
-
-function action_confirm()
-       local uci = require "luci.model.uci"
-       local token = luci.http.formvalue("token")
-       local _, errstr = uci:confirm(token)
-       ubus_state_to_http(errstr)
-end
-
-function action_revert()
-       local uci = require "luci.model.uci"
-       local changes = uci:changes()
-
-       -- Collect files to be reverted
-       local _, errstr, r, tbl
-       for r, tbl in pairs(changes) do
-               _, errstr = uci:revert(r)
-               if errstr then
-                       break
-               end
-       end
-
-       ubus_state_to_http(errstr or "OK")
-end
diff --git a/modules/luci-base/luasrc/dispatcher.lua b/modules/luci-base/luasrc/dispatcher.lua
deleted file mode 100644 (file)
index a3726fb..0000000
+++ /dev/null
@@ -1,1564 +0,0 @@
--- Copyright 2008 Steven Barth <steven@midlink.org>
--- Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org>
--- Licensed to the public under the Apache License 2.0.
-
-local fs = require "nixio.fs"
-local sys = require "luci.sys"
-local util = require "luci.util"
-local xml = require "luci.xml"
-local http = require "luci.http"
-local nixio = require "nixio", require "nixio.util"
-
-module("luci.dispatcher", package.seeall)
-context = util.threadlocal()
-uci = require "luci.model.uci"
-i18n = require "luci.i18n"
-_M.fs = fs
-
--- Index table
-local index = nil
-
-local function check_fs_depends(spec)
-       local fs = require "nixio.fs"
-
-       for path, kind in pairs(spec) do
-               if kind == "directory" then
-                       local empty = true
-                       for entry in (fs.dir(path) or function() end) do
-                               empty = false
-                               break
-                       end
-                       if empty then
-                               return false
-                       end
-               elseif kind == "executable" then
-                       if fs.stat(path, "type") ~= "reg" or not fs.access(path, "x") then
-                               return false
-                       end
-               elseif kind == "file" then
-                       if fs.stat(path, "type") ~= "reg" then
-                               return false
-                       end
-               elseif kind == "absent" then
-                       if fs.stat(path, "type") then
-                               return false
-                       end
-               end
-       end
-
-       return true
-end
-
-local function check_uci_depends_options(conf, s, opts)
-       local uci = require "luci.model.uci"
-
-       if type(opts) == "string" then
-               return (s[".type"] == opts)
-       elseif opts == true then
-               for option, value in pairs(s) do
-                       if option:byte(1) ~= 46 then
-                               return true
-                       end
-               end
-       elseif type(opts) == "table" then
-               for option, value in pairs(opts) do
-                       local sval = s[option]
-                       if type(sval) == "table" then
-                               local found = false
-                               for _, v in ipairs(sval) do
-                                       if v == value then
-                                               found = true
-                                               break
-                                       end
-                               end
-                               if not found then
-                                       return false
-                               end
-                       elseif value == true then
-                               if sval == nil then
-                                       return false
-                               end
-                       else
-                               if sval ~= value then
-                                       return false
-                               end
-                       end
-               end
-       end
-
-       return true
-end
-
-local function check_uci_depends_section(conf, sect)
-       local uci = require "luci.model.uci"
-
-       for section, options in pairs(sect) do
-               local stype = section:match("^@([A-Za-z0-9_%-]+)$")
-               if stype then
-                       local found = false
-                       uci:foreach(conf, stype, function(s)
-                               if check_uci_depends_options(conf, s, options) then
-                                       found = true
-                                       return false
-                               end
-                       end)
-                       if not found then
-                               return false
-                       end
-               else
-                       local s = uci:get_all(conf, section)
-                       if not s or not check_uci_depends_options(conf, s, options) then
-                               return false
-                       end
-               end
-       end
-
-       return true
-end
-
-local function check_uci_depends(conf)
-       local uci = require "luci.model.uci"
-
-       for config, values in pairs(conf) do
-               if values == true then
-                       local found = false
-                       uci:foreach(config, nil, function(s)
-                               found = true
-                               return false
-                       end)
-                       if not found then
-                               return false
-                       end
-               elseif type(values) == "table" then
-                       if not check_uci_depends_section(config, values) then
-                               return false
-                       end
-               end
-       end
-
-       return true
-end
-
-local function check_acl_depends(require_groups, groups)
-       if type(require_groups) == "table" and #require_groups > 0 then
-               local writable = false
-
-               for _, group in ipairs(require_groups) do
-                       local read = false
-                       local write = false
-                       if type(groups) == "table" and type(groups[group]) == "table" then
-                               for _, perm in ipairs(groups[group]) do
-                                       if perm == "read" then
-                                               read = true
-                                       elseif perm == "write" then
-                                               write = true
-                                       end
-                               end
-                       end
-                       if not read and not write then
-                               return nil
-                       elseif write then
-                               writable = true
-                       end
-               end
-
-               return writable
-       end
-
-       return true
-end
-
-local function check_depends(spec)
-       if type(spec.depends) ~= "table" then
-               return true
-       end
-
-       if type(spec.depends.fs) == "table" then
-               local satisfied = false
-               local alternatives = (#spec.depends.fs > 0) and spec.depends.fs or { spec.depends.fs }
-               for _, alternative in ipairs(alternatives) do
-                       if check_fs_depends(alternative) then
-                               satisfied = true
-                               break
-                       end
-               end
-               if not satisfied then
-                       return false
-               end
-       end
-
-       if type(spec.depends.uci) == "table" then
-               local satisfied = false
-               local alternatives = (#spec.depends.uci > 0) and spec.depends.uci or { spec.depends.uci }
-               for _, alternative in ipairs(alternatives) do
-                       if check_uci_depends(alternative) then
-                               satisfied = true
-                               break
-                       end
-               end
-               if not satisfied then
-                       return false
-               end
-       end
-
-       return true
-end
-
-local function target_to_json(target, module)
-       local action
-
-       if target.type == "call" then
-               action = {
-                       ["type"] = "call",
-                       ["module"] = module,
-                       ["function"] = target.name,
-                       ["parameters"] = target.argv
-               }
-       elseif target.type == "view" then
-               action = {
-                       ["type"] = "view",
-                       ["path"] = target.view
-               }
-       elseif target.type == "template" then
-               action = {
-                       ["type"] = "template",
-                       ["path"] = target.view
-               }
-       elseif target.type == "cbi" then
-               action = {
-                       ["type"] = "cbi",
-                       ["path"] = target.model,
-                       ["config"] = target.config
-               }
-       elseif target.type == "form" then
-               action = {
-                       ["type"] = "form",
-                       ["path"] = target.model
-               }
-       elseif target.type == "firstchild" then
-               action = {
-                       ["type"] = "firstchild"
-               }
-       elseif target.type == "firstnode" then
-               action = {
-                       ["type"] = "firstchild",
-                       ["recurse"] = true
-               }
-       elseif target.type == "arcombine" then
-               if type(target.targets) == "table" then
-                       action = {
-                               ["type"] = "arcombine",
-                               ["targets"] = {
-                                       target_to_json(target.targets[1], module),
-                                       target_to_json(target.targets[2], module)
-                               }
-                       }
-               end
-       elseif target.type == "alias" then
-               action = {
-                       ["type"] = "alias",
-                       ["path"] = table.concat(target.req, "/")
-               }
-       elseif target.type == "rewrite" then
-               action = {
-                       ["type"] = "rewrite",
-                       ["path"] = table.concat(target.req, "/"),
-                       ["remove"] = target.n
-               }
-       end
-
-       if target.post and action then
-               action.post = target.post
-       end
-
-       return action
-end
-
-local function tree_to_json(node, json)
-       local fs = require "nixio.fs"
-       local util = require "luci.util"
-
-       if type(node.nodes) == "table" then
-               for subname, subnode in pairs(node.nodes) do
-                       local spec = {
-                               title = xml.striptags(subnode.title),
-                               order = subnode.order
-                       }
-
-                       if subnode.leaf then
-                               spec.wildcard = true
-                       end
-
-                       if subnode.cors then
-                               spec.cors = true
-                       end
-
-                       if subnode.setuser then
-                               spec.setuser = subnode.setuser
-                       end
-
-                       if subnode.setgroup then
-                               spec.setgroup = subnode.setgroup
-                       end
-
-                       if type(subnode.target) == "table" then
-                               spec.action = target_to_json(subnode.target, subnode.module)
-                       end
-
-                       if type(subnode.file_depends) == "table" then
-                               for _, v in ipairs(subnode.file_depends) do
-                                       spec.depends = spec.depends or {}
-                                       spec.depends.fs = spec.depends.fs or {}
-
-                                       local ft = fs.stat(v, "type")
-                                       if ft == "dir" then
-                                               spec.depends.fs[v] = "directory"
-                                       elseif v:match("/s?bin/") then
-                                               spec.depends.fs[v] = "executable"
-                                       else
-                                               spec.depends.fs[v] = "file"
-                                       end
-                               end
-                       end
-
-                       if type(subnode.uci_depends) == "table" then
-                               for k, v in pairs(subnode.uci_depends) do
-                                       spec.depends = spec.depends or {}
-                                       spec.depends.uci = spec.depends.uci or {}
-                                       spec.depends.uci[k] = v
-                               end
-                       end
-
-                       if type(subnode.acl_depends) == "table" then
-                               for _, acl in ipairs(subnode.acl_depends) do
-                                       spec.depends = spec.depends or {}
-                                       spec.depends.acl = spec.depends.acl or {}
-                                       spec.depends.acl[#spec.depends.acl + 1] = acl
-                               end
-                       end
-
-                       if (subnode.sysauth_authenticator ~= nil) or
-                          (subnode.sysauth ~= nil and subnode.sysauth ~= false)
-                       then
-                               if subnode.sysauth_authenticator == "htmlauth" then
-                                       spec.auth = {
-                                               login = true,
-                                               methods = { "cookie:sysauth_https", "cookie:sysauth_http" }
-                                       }
-                               elseif subname == "rpc" and subnode.module == "luci.controller.rpc" then
-                                       spec.auth = {
-                                               login = false,
-                                               methods = { "query:auth", "cookie:sysauth_https", "cookie:sysauth_http" }
-                                       }
-                               elseif subnode.module == "luci.controller.admin.uci" then
-                                       spec.auth = {
-                                               login = false,
-                                               methods = { "param:sid" }
-                                       }
-                               end
-                       elseif subnode.sysauth == false then
-                               spec.auth = {}
-                       end
-
-                       if not spec.action then
-                               spec.title = nil
-                       end
-
-                       spec.satisfied = check_depends(spec)
-                       json.children = json.children or {}
-                       json.children[subname] = tree_to_json(subnode, spec)
-               end
-       end
-
-       return json
-end
-
-function build_url(...)
-       local path = {...}
-       local url = { http.getenv("SCRIPT_NAME") or "" }
-
-       local p
-       for _, p in ipairs(path) do
-               if p:match("^[a-zA-Z0-9_%-%.%%/,;]+$") then
-                       url[#url+1] = "/"
-                       url[#url+1] = p
-               end
-       end
-
-       if #path == 0 then
-               url[#url+1] = "/"
-       end
-
-       return table.concat(url, "")
-end
-
-
-function error404(message)
-       http.status(404, "Not Found")
-       message = message or "Not Found"
-
-       local function render()
-               local template = require "luci.template"
-               template.render("error404", {message=message})
-       end
-
-       if not util.copcall(render) then
-               http.prepare_content("text/plain")
-               http.write(message)
-       end
-
-       return false
-end
-
-function error500(message)
-       util.perror(message)
-       if not context.template_header_sent then
-               http.status(500, "Internal Server Error")
-               http.prepare_content("text/plain")
-               http.write(message)
-       else
-               require("luci.template")
-               if not util.copcall(luci.template.render, "error500", {message=message}) then
-                       http.prepare_content("text/plain")
-                       http.write(message)
-               end
-       end
-       return false
-end
-
-local function determine_request_language()
-       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 or "auto"
-       if lang == "auto" then
-               local aclang = http.getenv("HTTP_ACCEPT_LANGUAGE") or ""
-               for aclang in aclang:gmatch("[%w_-]+") do
-                       local country, culture = aclang:match("^([a-z][a-z])[_-]([a-zA-Z][a-zA-Z])$")
-                       if country and culture then
-                               local cc = "%s_%s" %{ country, culture:lower() }
-                               if conf.languages[cc] then
-                                       lang = cc
-                                       break
-                               elseif conf.languages[country] then
-                                       lang = country
-                                       break
-                               end
-                       elseif conf.languages[aclang] then
-                               lang = aclang
-                               break
-                       end
-               end
-       end
-
-       if lang == "auto" then
-               lang = i18n.default
-       end
-
-       i18n.setlanguage(lang)
-end
-
-function httpdispatch(request, prefix)
-       http.context.request = request
-
-       local r = {}
-       context.request = r
-
-       local pathinfo = http.urldecode(request:getenv("PATH_INFO") or "", true)
-
-       if prefix then
-               for _, node in ipairs(prefix) do
-                       r[#r+1] = node
-               end
-       end
-
-       local node
-       for node in pathinfo:gmatch("[^/%z]+") do
-               r[#r+1] = node
-       end
-
-       determine_request_language()
-
-       local stat, err = util.coxpcall(function()
-               dispatch(context.request)
-       end, error500)
-
-       http.close()
-
-       --context._disable_memtrace()
-end
-
-local function require_post_security(target, args)
-       if type(target) == "table" and target.type == "arcombine" and type(target.targets) == "table" then
-               return require_post_security((type(args) == "table" and #args > 0) and target.targets[2] or target.targets[1], args)
-       end
-
-       if type(target) == "table" then
-               if type(target.post) == "table" then
-                       local param_name, required_val, request_val
-
-                       for param_name, required_val in pairs(target.post) do
-                               request_val = http.formvalue(param_name)
-
-                               if (type(required_val) == "string" and
-                                   request_val ~= required_val) or
-                                  (required_val == true and request_val == nil)
-                               then
-                                       return false
-                               end
-                       end
-
-                       return true
-               end
-
-               return (target.post == true)
-       end
-
-       return false
-end
-
-function test_post_security()
-       if http.getenv("REQUEST_METHOD") ~= "POST" then
-               http.status(405, "Method Not Allowed")
-               http.header("Allow", "POST")
-               return false
-       end
-
-       if http.formvalue("token") ~= context.authtoken then
-               http.status(403, "Forbidden")
-               luci.template.render("csrftoken")
-               return false
-       end
-
-       return true
-end
-
-local function session_retrieve(sid, allowed_users)
-       local sdat = util.ubus("session", "get", { ubus_rpc_session = sid })
-       local sacl = util.ubus("session", "access", { ubus_rpc_session = sid })
-
-       if type(sdat) == "table" and
-          type(sdat.values) == "table" and
-          type(sdat.values.token) == "string" and
-          (not allowed_users or
-           util.contains(allowed_users, sdat.values.username))
-       then
-               uci:set_session_id(sid)
-               return sid, sdat.values, type(sacl) == "table" and sacl or {}
-       end
-
-       return nil, nil, nil
-end
-
-local function session_setup(user, pass)
-       local login = util.ubus("session", "login", {
-               username = user,
-               password = pass,
-               timeout  = tonumber(luci.config.sauth.sessiontime)
-       })
-
-       local rp = context.requestpath
-               and table.concat(context.requestpath, "/") or ""
-
-       if type(login) == "table" and
-          type(login.ubus_rpc_session) == "string"
-       then
-               util.ubus("session", "set", {
-                       ubus_rpc_session = login.ubus_rpc_session,
-                       values = { token = sys.uniqueid(16) }
-               })
-               nixio.syslog("info", tostring("luci: accepted login on /%s for %s from %s\n"
-                       %{ rp, user or "?", http.getenv("REMOTE_ADDR") or "?" }))
-
-               return session_retrieve(login.ubus_rpc_session)
-       end
-       nixio.syslog("info", tostring("luci: failed login on /%s for %s from %s\n"
-               %{ rp, user or "?", http.getenv("REMOTE_ADDR") or "?" }))
-end
-
-local function check_authentication(method)
-       local auth_type, auth_param = method:match("^(%w+):(.+)$")
-       local sid, sdat
-
-       if auth_type == "cookie" then
-               sid = http.getcookie(auth_param)
-       elseif auth_type == "param" then
-               sid = http.formvalue(auth_param)
-       elseif auth_type == "query" then
-               sid = http.formvalue(auth_param, true)
-       end
-
-       return session_retrieve(sid)
-end
-
-local function merge_trees(node_a, node_b)
-       for k, v in pairs(node_b) do
-               if k == "children" then
-                       node_a.children = node_a.children or {}
-
-                       for name, spec in pairs(v) do
-                               node_a.children[name] = merge_trees(node_a.children[name] or {}, spec)
-                       end
-               else
-                       node_a[k] = v
-               end
-       end
-
-       if type(node_a.action) == "table" and
-          node_a.action.type == "firstchild" and
-          node_a.children == nil
-       then
-               node_a.satisfied = false
-       end
-
-       return node_a
-end
-
-local function apply_tree_acls(node, acl)
-       if type(node.children) == "table" then
-               for _, child in pairs(node.children) do
-                       apply_tree_acls(child, acl)
-               end
-       end
-
-       local perm
-       if type(node.depends) == "table" then
-               perm = check_acl_depends(node.depends.acl, acl["access-group"])
-       else
-               perm = true
-       end
-
-       if perm == nil then
-               node.satisfied = false
-       elseif perm == false then
-               node.readonly = true
-       end
-end
-
-function menu_json(acl)
-       local tree = context.tree or createtree()
-       local lua_tree = tree_to_json(tree, {
-               action = {
-                       ["type"] = "firstchild",
-                       ["recurse"] = true
-               }
-       })
-
-       local json_tree = createtree_json()
-       local menu_tree = merge_trees(lua_tree, json_tree)
-
-       if acl then
-               apply_tree_acls(menu_tree, acl)
-       end
-
-       return menu_tree
-end
-
-local function init_template_engine(ctx)
-       local tpl = require "luci.template"
-       local media = luci.config.main.mediaurlbase
-
-       if not pcall(tpl.Template, "themes/%s/header" % fs.basename(media)) then
-               media = nil
-               for name, theme in pairs(luci.config.themes) do
-                       if name:sub(1,1) ~= "." and pcall(tpl.Template,
-                        "themes/%s/header" % fs.basename(theme)) then
-                               media = theme
-                       end
-               end
-               assert(media, "No valid theme found")
-       end
-
-       local function _ifattr(cond, key, val, noescape)
-               if cond then
-                       local env = getfenv(3)
-                       local scope = (type(env.self) == "table") and env.self
-                       if type(val) == "table" then
-                               if not next(val) then
-                                       return ''
-                               else
-                                       val = util.serialize_json(val)
-                               end
-                       end
-
-                       val = tostring(val or
-                               (type(env[key]) ~= "function" and env[key]) or
-                               (scope and type(scope[key]) ~= "function" and scope[key]) or "")
-
-                       if noescape ~= true then
-                               val = xml.pcdata(val)
-                       end
-
-                       return string.format(' %s="%s"', tostring(key), val)
-               else
-                       return ''
-               end
-       end
-
-       tpl.context.viewns = setmetatable({
-               write       = http.write;
-               include     = function(name) tpl.Template(name):render(getfenv(2)) end;
-               translate   = i18n.translate;
-               translatef  = i18n.translatef;
-               export      = function(k, v) if tpl.context.viewns[k] == nil then tpl.context.viewns[k] = v end end;
-               striptags   = xml.striptags;
-               pcdata      = xml.pcdata;
-               media       = media;
-               theme       = fs.basename(media);
-               resource    = luci.config.main.resourcebase;
-               ifattr      = function(...) return _ifattr(...) end;
-               attr        = function(...) return _ifattr(true, ...) end;
-               url         = build_url;
-       }, {__index=function(tbl, key)
-               if key == "controller" then
-                       return build_url()
-               elseif key == "REQUEST_URI" then
-                       return build_url(unpack(ctx.requestpath))
-               elseif key == "FULL_REQUEST_URI" then
-                       local url = { http.getenv("SCRIPT_NAME") or "", http.getenv("PATH_INFO") }
-                       local query = http.getenv("QUERY_STRING")
-                       if query and #query > 0 then
-                               url[#url+1] = "?"
-                               url[#url+1] = query
-                       end
-                       return table.concat(url, "")
-               elseif key == "token" then
-                       return ctx.authtoken
-               else
-                       return rawget(tbl, key) or _G[key]
-               end
-       end})
-
-       return tpl
-end
-
-function is_authenticated(auth)
-       if type(auth) == "table" and type(auth.methods) == "table" and #auth.methods > 0 then
-               local sid, sdat, sacl
-               for _, method in ipairs(auth.methods) do
-                       sid, sdat, sacl = check_authentication(method)
-
-                       if sid and sdat and sacl then
-                               return sid, sdat, sacl
-                       end
-               end
-       end
-end
-
-local function ctx_append(ctx, name, node)
-       ctx.path = ctx.path or {}
-       ctx.path[#ctx.path + 1] = name
-
-       ctx.acls = ctx.acls or {}
-
-       local acls = (type(node.depends) == "table" and type(node.depends.acl) == "table") and node.depends.acl or {}
-       for _, acl in ipairs(acls) do
-               ctx.acls[_] = acl
-       end
-
-       ctx.auth = node.auth or ctx.auth
-       ctx.cors = node.cors or ctx.cors
-       ctx.suid = node.setuser or ctx.suid
-       ctx.sgid = node.setgroup or ctx.sgid
-
-       return ctx
-end
-
-local function node_weight(node)
-       local weight = node.order or 9999
-
-       if weight > 9999 then
-               weight = 9999
-       end
-
-       if type(node.auth) == "table" and node.auth.login then
-               weight = weight + 10000
-       end
-
-       return weight
-end
-
-local function resolve_firstchild(node, sacl, login_allowed, ctx)
-       local candidate = nil
-       local candidate_ctx = nil
-
-       for name, child in pairs(node.children) do
-               if child.satisfied then
-                       if not sacl then
-                               local _
-                               _, _, sacl = is_authenticated(node.auth)
-                       end
-
-                       local cacl = (type(child.depends) == "table") and child.depends.acl or nil
-                       local login = login_allowed or (type(child.auth) == "table" and child.auth.login)
-                       if login or check_acl_depends(cacl, sacl and sacl["access-group"]) ~= nil then
-                               if child.title and type(child.action) == "table" then
-                                       local child_ctx = ctx_append(util.clone(ctx, true), name, child)
-                                       if child.action.type == "firstchild" then
-                                               if not candidate or node_weight(candidate) > node_weight(child) then
-                                                       local have_grandchild = resolve_firstchild(child, sacl, login, child_ctx)
-                                                       if have_grandchild then
-                                                               candidate = child
-                                                               candidate_ctx = child_ctx
-                                                       end
-                                               end
-                                       elseif not child.firstchild_ineligible then
-                                               if not candidate or node_weight(candidate) > node_weight(child) then
-                                                       candidate = child
-                                                       candidate_ctx = child_ctx
-                                               end
-                                       end
-                               end
-                       end
-               end
-       end
-
-       if candidate then
-               for k, v in pairs(candidate_ctx) do
-                       ctx[k] = v
-               end
-
-               return true
-       end
-
-       return false
-end
-
-local function resolve_page(tree, request_path)
-       local node = tree
-       local sacl = nil
-       local login = false
-       local ctx = {}
-
-       for i, s in ipairs(request_path) do
-               node = node.children and node.children[s]
-
-               if not node or not node.satisfied then
-                       break
-               end
-
-               ctx_append(ctx, s, node)
-
-               if not sacl then
-                       local _
-                       _, _, sacl = is_authenticated(node.auth)
-               end
-
-               if not login and type(node.auth) == "table" and node.auth.login then
-                       login = true
-               end
-
-               if node.wildcard then
-                       ctx.request_args = {}
-                       ctx.request_path = util.clone(ctx.path, true)
-
-                       for j = i + 1, #request_path do
-                               ctx.request_path[j] = request_path[j]
-                               ctx.request_args[j - i] = request_path[j]
-                       end
-
-                       break
-               end
-       end
-
-       if node and type(node.action) == "table" and node.action.type == "firstchild" then
-               resolve_firstchild(node, sacl, login, ctx)
-       end
-
-       ctx.acls = ctx.acls or {}
-       ctx.path = ctx.path or {}
-       ctx.request_args = ctx.request_args or {}
-       ctx.request_path = ctx.request_path or util.clone(request_path, true)
-
-       node = tree
-
-       for _, s in ipairs(ctx.path or {}) do
-               node = node.children[s]
-               assert(node, "Internal node resolve error")
-       end
-
-       return node, ctx
-end
-
-function dispatch(request)
-       --context._disable_memtrace = require "luci.debug".trap_memtrace("l")
-       local ctx = context
-
-       local auth, cors, suid, sgid
-       local menu = menu_json()
-       local page, lookup_ctx = resolve_page(menu, request)
-       local action = (page and type(page.action) == "table") and page.action or {}
-
-       local tpl = init_template_engine(ctx)
-
-       ctx.args = lookup_ctx.request_args
-       ctx.path = lookup_ctx.path
-       ctx.dispatched = page
-
-       ctx.requestpath = ctx.requestpath or lookup_ctx.request_path
-       ctx.requestargs = ctx.requestargs or lookup_ctx.request_args
-       ctx.requested = ctx.requested or page
-
-       if type(lookup_ctx.auth) == "table" and next(lookup_ctx.auth) then
-               local sid, sdat, sacl = is_authenticated(lookup_ctx.auth)
-
-               if not (sid and sdat and sacl) and lookup_ctx.auth.login then
-                       local user = http.getenv("HTTP_AUTH_USER")
-                       local pass = http.getenv("HTTP_AUTH_PASS")
-
-                       if user == nil and pass == nil then
-                               user = http.formvalue("luci_username")
-                               pass = http.formvalue("luci_password")
-                       end
-
-                       if user and pass then
-                               sid, sdat, sacl = session_setup(user, pass)
-                       end
-
-                       if not sid then
-                               context.path = {}
-
-                               http.status(403, "Forbidden")
-                               http.header("X-LuCI-Login-Required", "yes")
-
-                               local scope = { duser = "root", fuser = user }
-                               local ok, res = util.copcall(tpl.render_string, [[<% include("themes/" .. theme .. "/sysauth") %>]], scope)
-                               if ok then
-                                       return res
-                               end
-                               return tpl.render("sysauth", scope)
-                       end
-
-                       http.header("Set-Cookie", 'sysauth_%s=%s; path=%s; SameSite=Strict; HttpOnly%s' %{
-                               http.getenv("HTTPS") == "on" and "https" or "http",
-                               sid, build_url(), http.getenv("HTTPS") == "on" and "; secure" or ""
-                       })
-
-                       http.redirect(build_url(unpack(ctx.requestpath)))
-                       return
-               end
-
-               if not sid or not sdat or not sacl then
-                       http.status(403, "Forbidden")
-                       http.header("X-LuCI-Login-Required", "yes")
-                       return
-               end
-
-               ctx.authsession = sid
-               ctx.authtoken = sdat.token
-               ctx.authuser = sdat.username
-               ctx.authacl = sacl
-       end
-
-       if #lookup_ctx.acls > 0 then
-               local perm = check_acl_depends(lookup_ctx.acls, ctx.authacl and ctx.authacl["access-group"])
-               if perm == nil then
-                       http.status(403, "Forbidden")
-                       return
-               end
-
-               if page then
-                       page.readonly = not perm
-               end
-       end
-
-       if action.type == "arcombine" then
-               action = (#lookup_ctx.request_args > 0) and action.targets[2] or action.targets[1]
-       end
-
-       if lookup_ctx.cors and http.getenv("REQUEST_METHOD") == "OPTIONS" then
-               luci.http.status(200, "OK")
-               luci.http.header("Access-Control-Allow-Origin", http.getenv("HTTP_ORIGIN") or "*")
-               luci.http.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
-               return
-       end
-
-       if require_post_security(action) then
-               if not test_post_security() then
-                       return
-               end
-       end
-
-       if lookup_ctx.sgid then
-               sys.process.setgroup(lookup_ctx.sgid)
-       end
-
-       if lookup_ctx.suid then
-               sys.process.setuser(lookup_ctx.suid)
-       end
-
-       if action.type == "view" then
-               tpl.render("view", { view = action.path })
-
-       elseif action.type == "call" then
-               local ok, mod = util.copcall(require, action.module)
-               if not ok then
-                       error500(mod)
-                       return
-               end
-
-               local func = mod[action["function"]]
-
-               assert(func ~= nil,
-                      'Cannot resolve function "' .. action["function"] .. '". Is it misspelled or local?')
-
-               assert(type(func) == "function",
-                      'The symbol "' .. action["function"] .. '" does not refer to a function but data ' ..
-                      'of type "' .. type(func) .. '".')
-
-               local argv = (type(action.parameters) == "table" and #action.parameters > 0) and { unpack(action.parameters) } or {}
-               for _, s in ipairs(lookup_ctx.request_args) do
-                       argv[#argv + 1] = s
-               end
-
-               local ok, err = util.copcall(func, unpack(argv))
-               if not ok then
-                       error500(err)
-               end
-
-       --elseif action.type == "firstchild" then
-       --      tpl.render("empty_node_placeholder", getfenv(1))
-
-       elseif action.type == "alias" then
-               local sub_request = {}
-               for name in action.path:gmatch("[^/]+") do
-                       sub_request[#sub_request + 1] = name
-               end
-
-               for _, s in ipairs(lookup_ctx.request_args) do
-                       sub_request[#sub_request + 1] = s
-               end
-
-               dispatch(sub_request)
-
-       elseif action.type == "rewrite" then
-               local sub_request = { unpack(request) }
-               for i = 1, action.remove do
-                       table.remove(sub_request, 1)
-               end
-
-               local n = 1
-               for s in action.path:gmatch("[^/]+") do
-                       table.insert(sub_request, n, s)
-                       n = n + 1
-               end
-
-               for _, s in ipairs(lookup_ctx.request_args) do
-                       sub_request[#sub_request + 1] = s
-               end
-
-               dispatch(sub_request)
-
-       elseif action.type == "template" then
-               tpl.render(action.path, getfenv(1))
-
-       elseif action.type == "cbi" then
-               _cbi({ config = action.config, model = action.path }, unpack(lookup_ctx.request_args))
-
-       elseif action.type == "form" then
-               _form({ model = action.path }, unpack(lookup_ctx.request_args))
-
-       else
-               if not menu.children then
-                       error404("No root node was registered, this usually happens if no module was installed.\n" ..
-                                "Install luci-mod-admin-full and retry. " ..
-                                "If the module is already installed, try removing the /tmp/luci-indexcache file.")
-               else
-                       error404("No page is registered at '/" .. table.concat(lookup_ctx.request_path, "/") .. "'.\n" ..
-                                "If this url belongs to an extension, make sure it is properly installed.\n" ..
-                                "If the extension was recently installed, try removing the /tmp/luci-indexcache file.")
-               end
-       end
-end
-
-local function hash_filelist(files)
-       local fprint = {}
-       local n = 0
-
-       for i, file in ipairs(files) do
-               local st = fs.stat(file)
-               if st then
-                       fprint[n + 1] = '%x' % st.ino
-                       fprint[n + 2] = '%x' % st.mtime
-                       fprint[n + 3] = '%x' % st.size
-                       n = n + 3
-               end
-       end
-
-       return nixio.crypt(table.concat(fprint, "|"), "$1$"):sub(5):gsub("/", ".")
-end
-
-local function read_cachefile(file, reader)
-       local euid = sys.process.info("uid")
-       local fuid = fs.stat(file, "uid")
-       local mode = fs.stat(file, "modestr")
-
-       if euid ~= fuid or mode ~= "rw-------" then
-               return nil
-       end
-
-       return reader(file)
-end
-
-function createindex()
-       local controllers = { }
-       local base = "%s/controller/" % util.libpath()
-       local _, path
-
-       for path in (fs.glob("%s*.lua" % base) or function() end) do
-               controllers[#controllers+1] = path
-       end
-
-       for path in (fs.glob("%s*/*.lua" % base) or function() end) do
-               controllers[#controllers+1] = path
-       end
-
-       local cachefile
-
-       if indexcache then
-               cachefile = "%s.%s.lua" %{ indexcache, hash_filelist(controllers) }
-
-               local res = read_cachefile(cachefile, function(path) return loadfile(path)() end)
-               if res then
-                       index = res
-                       return res
-               end
-
-               for file in (fs.glob("%s.*.lua" % indexcache) or function() end) do
-                       fs.unlink(file)
-               end
-       end
-
-       index = {}
-
-       for _, path in ipairs(controllers) do
-               local modname = "luci.controller." .. path:sub(#base+1, #path-4):gsub("/", ".")
-               local mod = require(modname)
-               assert(mod ~= true,
-                      "Invalid controller file found\n" ..
-                      "The file '" .. path .. "' contains an invalid module line.\n" ..
-                      "Please verify whether the module name is set to '" .. modname ..
-                      "' - It must correspond to the file path!")
-
-               local idx = mod.index
-               if type(idx) == "function" then
-                       index[modname] = idx
-               end
-       end
-
-       if cachefile then
-               local f = nixio.open(cachefile, "w", 600)
-               f:writeall(util.get_bytecode(index))
-               f:close()
-       end
-end
-
-function createtree_json()
-       local json = require "luci.jsonc"
-       local tree = {}
-
-       local schema = {
-               action = "table",
-               auth = "table",
-               cors = "boolean",
-               depends = "table",
-               order = "number",
-               setgroup = "string",
-               setuser = "string",
-               title = "string",
-               wildcard = "boolean",
-               firstchild_ineligible = "boolean"
-       }
-
-       local files = {}
-       local cachefile
-
-       for file in (fs.glob("/usr/share/luci/menu.d/*.json") or function() end) do
-               files[#files+1] = file
-       end
-
-       if indexcache then
-               cachefile = "%s.%s.json" %{ indexcache, hash_filelist(files) }
-
-               local res = read_cachefile(cachefile, function(path) return json.parse(fs.readfile(path) or "") end)
-               if res then
-                       return res
-               end
-
-               for file in (fs.glob("%s.*.json" % indexcache) or function() end) do
-                       fs.unlink(file)
-               end
-       end
-
-       for _, file in ipairs(files) do
-               local data = json.parse(fs.readfile(file) or "")
-               if type(data) == "table" then
-                       for path, spec in pairs(data) do
-                               if type(spec) == "table" then
-                                       local node = tree
-
-                                       for s in path:gmatch("[^/]+") do
-                                               if s == "*" then
-                                                       node.wildcard = true
-                                                       break
-                                               end
-
-                                               node.children = node.children or {}
-                                               node.children[s] = node.children[s] or {}
-                                               node = node.children[s]
-                                       end
-
-                                       if node ~= tree then
-                                               for k, t in pairs(schema) do
-                                                       if type(spec[k]) == t then
-                                                               node[k] = spec[k]
-                                                       end
-                                               end
-
-                                               node.satisfied = check_depends(spec)
-                                       end
-                               end
-                       end
-               end
-       end
-
-       if cachefile then
-               local f = nixio.open(cachefile, "w", 600)
-               f:writeall(json.stringify(tree))
-               f:close()
-       end
-
-       return tree
-end
-
--- Build the index before if it does not exist yet.
-function createtree()
-       if not index then
-               createindex()
-       end
-
-       local ctx  = context
-       local tree = {nodes={}, inreq=true}
-
-       ctx.treecache = setmetatable({}, {__mode="v"})
-       ctx.tree = tree
-
-       local scope = setmetatable({}, {__index = luci.dispatcher})
-
-       for k, v in pairs(index) do
-               scope._NAME = k
-               setfenv(v, scope)
-               v()
-       end
-
-       return tree
-end
-
-function assign(path, clone, title, order)
-       local obj  = node(unpack(path))
-       obj.nodes  = nil
-       obj.module = nil
-
-       obj.title = title
-       obj.order = order
-
-       setmetatable(obj, {__index = _create_node(clone)})
-
-       return obj
-end
-
-function entry(path, target, title, order)
-       local c = node(unpack(path))
-
-       c.target = target
-       c.title  = title
-       c.order  = order
-       c.module = getfenv(2)._NAME
-
-       return c
-end
-
--- enabling the node.
-function get(...)
-       return _create_node({...})
-end
-
-function node(...)
-       local c = _create_node({...})
-
-       c.module = getfenv(2)._NAME
-       c.auto = nil
-
-       return c
-end
-
-function lookup(...)
-       local i, path = nil, {}
-       for i = 1, select('#', ...) do
-               local name, arg = nil, tostring(select(i, ...))
-               for name in arg:gmatch("[^/]+") do
-                       path[#path+1] = name
-               end
-       end
-
-       for i = #path, 1, -1 do
-               local node = context.treecache[table.concat(path, ".", 1, i)]
-               if node and (i == #path or node.leaf) then
-                       return node, build_url(unpack(path))
-               end
-       end
-end
-
-function _create_node(path)
-       if #path == 0 then
-               return context.tree
-       end
-
-       local name = table.concat(path, ".")
-       local c = context.treecache[name]
-
-       if not c then
-               local last = table.remove(path)
-               local parent = _create_node(path)
-
-               c = {nodes={}, auto=true, inreq=true}
-
-               parent.nodes[last] = c
-               context.treecache[name] = c
-       end
-
-       return c
-end
-
--- Subdispatchers --
-
-function firstchild()
-       return { type = "firstchild" }
-end
-
-function firstnode()
-       return { type = "firstnode" }
-end
-
-function alias(...)
-       return { type = "alias", req = { ... } }
-end
-
-function rewrite(n, ...)
-       return { type = "rewrite", n = n, req = { ... } }
-end
-
-function call(name, ...)
-       return { type = "call", argv = {...}, name = name }
-end
-
-function post_on(params, name, ...)
-       return {
-               type = "call",
-               post = params,
-               argv = { ... },
-               name = name
-       }
-end
-
-function post(...)
-       return post_on(true, ...)
-end
-
-
-function template(name)
-       return { type = "template", view = name }
-end
-
-function view(name)
-       return { type = "view", view = name }
-end
-
-
-function _cbi(self, ...)
-       local cbi = require "luci.cbi"
-       local tpl = require "luci.template"
-       local http = require "luci.http"
-       local util = require "luci.util"
-
-       local config = self.config or {}
-       local maps = cbi.load(self.model, ...)
-
-       local state = nil
-
-       local function has_uci_access(config, level)
-               local rv = util.ubus("session", "access", {
-                       ubus_rpc_session = context.authsession,
-                       scope = "uci", object = config,
-                       ["function"] = level
-               })
-
-               return (type(rv) == "table" and rv.access == true) or false
-       end
-
-       local i, res
-       for i, res in ipairs(maps) do
-               if util.instanceof(res, cbi.SimpleForm) then
-                       io.stderr:write("Model %s returns SimpleForm but is dispatched via cbi(),\n"
-                               % self.model)
-
-                       io.stderr:write("please change %s to use the form() action instead.\n"
-                               % table.concat(context.request, "/"))
-               end
-
-               res.flow = config
-               local cstate = res:parse()
-               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(_resolve_path(config.on_valid_to))
-               return
-       end
-
-       if config.on_changed_to and state and state > 1 then
-               http.redirect(_resolve_path(config.on_changed_to))
-               return
-       end
-
-       if config.on_success_to and state and state > 0 then
-               http.redirect(_resolve_path(config.on_success_to))
-               return
-       end
-
-       if config.state_handler then
-               if not config.state_handler(state, maps) then
-                       return
-               end
-       end
-
-       http.header("X-CBI-State", state or 0)
-
-       if not config.noheader then
-               tpl.render("cbi/header", {state = state})
-       end
-
-       local redirect
-       local messages
-       local applymap   = false
-       local pageaction = true
-       local parsechain = { }
-       local writable   = false
-
-       for i, res in ipairs(maps) do
-               if res.apply_needed and res.parsechain then
-                       local c
-                       for _, c in ipairs(res.parsechain) do
-                               parsechain[#parsechain+1] = c
-                       end
-                       applymap = true
-               end
-
-               if res.redirect then
-                       redirect = redirect or res.redirect
-               end
-
-               if res.pageaction == false then
-                       pageaction = false
-               end
-
-               if res.message then
-                       messages = messages or { }
-                       messages[#messages+1] = res.message
-               end
-       end
-
-       for i, res in ipairs(maps) do
-               local is_readable_map = has_uci_access(res.config, "read")
-               local is_writable_map = has_uci_access(res.config, "write")
-
-               writable = writable or is_writable_map
-
-               res:render({
-                       firstmap   = (i == 1),
-                       redirect   = redirect,
-                       messages   = messages,
-                       pageaction = pageaction,
-                       parsechain = parsechain,
-                       readable   = is_readable_map,
-                       writable   = is_writable_map
-               })
-       end
-
-       if not config.nofooter then
-               tpl.render("cbi/footer", {
-                       flow          = config,
-                       pageaction    = pageaction,
-                       redirect      = redirect,
-                       state         = state,
-                       autoapply     = config.autoapply,
-                       trigger_apply = applymap,
-                       writable      = writable
-               })
-       end
-end
-
-function cbi(model, config)
-       return {
-               type = "cbi",
-               post = { ["cbi.submit"] = true },
-               config = config,
-               model = model
-       }
-end
-
-
-function arcombine(trg1, trg2)
-       return {
-               type = "arcombine",
-               env = getfenv(),
-               targets = {trg1, trg2}
-       }
-end
-
-
-function _form(self, ...)
-       local cbi = require "luci.cbi"
-       local tpl = require "luci.template"
-       local http = require "luci.http"
-
-       local maps = luci.cbi.load(self.model, ...)
-       local state = nil
-
-       local i, res
-       for i, res in ipairs(maps) do
-               local cstate = res:parse()
-               if cstate and (not state or cstate < state) then
-                       state = cstate
-               end
-       end
-
-       http.header("X-CBI-State", state or 0)
-       tpl.render("header")
-       for i, res in ipairs(maps) do
-               res:render()
-       end
-       tpl.render("footer")
-end
-
-function form(model)
-       return {
-               type = "form",
-               post = { ["cbi.submit"] = true },
-               model = model
-       }
-end
-
-translate = i18n.translate
-
--- This function does not actually translate the given argument but
--- is used by build/i18n-scan.pl to find translatable entries.
-function _(text)
-       return text
-end
diff --git a/modules/luci-base/luasrc/dispatcher.luadoc b/modules/luci-base/luasrc/dispatcher.luadoc
deleted file mode 100644 (file)
index a77f8d8..0000000
+++ /dev/null
@@ -1,220 +0,0 @@
----[[
-LuCI web dispatcher.
-]]
-module "luci.dispatcher"
-
----[[
-Build the URL relative to the server webroot from given virtual path.
-
-@class function
-@name build_url
-@param ...     Virtual path
-@return                Relative URL
-]]
-
----[[
-Check whether a dispatch node shall be visible
-
-@class function
-@name node_visible
-@param node    Dispatch node
-@return                Boolean indicating whether the node should be visible
-]]
-
----[[
-Return a sorted table of visible children within a given node
-
-@class function
-@name node_childs
-@param node    Dispatch node
-@return                Ordered table of child node names
-]]
-
----[[
-Send a 404 error code and render the "error404" template if available.
-
-@class function
-@name error404
-@param message Custom error message (optional)
-@return                        false
-]]
-
----[[
-Send a 500 error code and render the "error500" template if available.
-
-@class function
-@name error500
-@param message Custom error message (optional)#
-@return                        false
-]]
-
----[[
-Dispatch an HTTP request.
-
-@class function
-@name httpdispatch
-@param request LuCI HTTP Request object
-]]
-
----[[
-Dispatches a LuCI virtual path.
-
-@class function
-@name dispatch
-@param request Virtual path
-]]
-
----[[
-Generate the dispatching index using the native file-cache based strategy.
-
-
-@class function
-@name createindex
-]]
-
----[[
-Create the dispatching tree from the index.
-
-Build the index before if it does not exist yet.
-
-@class function
-@name createtree
-]]
-
----[[
-Clone a node of the dispatching tree to another position.
-
-@class function
-@name assign
-@param path    Virtual path destination
-@param clone   Virtual path source
-@param title   Destination node title (optional)
-@param order   Destination node order value (optional)
-@return                        Dispatching tree node
-]]
-
----[[
-Create a new dispatching node and define common parameters.
-
-@class function
-@name entry
-@param path    Virtual path
-@param target  Target function to call when dispatched.
-@param title   Destination node title
-@param order   Destination node order value (optional)
-@return                        Dispatching tree node
-]]
-
----[[
-Fetch or create a dispatching node without setting the target module or
-enabling the node.
-
-@class function
-@name get
-@param ...             Virtual path
-@return                        Dispatching tree node
-]]
-
----[[
-Fetch or create a new dispatching node.
-
-@class function
-@name node
-@param ...             Virtual path
-@return                        Dispatching tree node
-]]
-
----[[
-Lookup node in dispatching tree.
-
-@class function
-@name lookup
-@param  ...            Virtual path
-@return Node object, canonical url or nil if the path was not found.
-]]
-
----[[
-Alias the first (lowest order) page automatically
-
-
-@class function
-@name firstchild
-]]
-
----[[
-Create a redirect to another dispatching node.
-
-@class function
-@name alias
-@param ...             Virtual path destination
-]]
-
----[[
-Rewrite the first x path values of the request.
-
-@class function
-@name rewrite
-@param n               Number of path values to replace
-@param ...             Virtual path to replace removed path values with
-]]
-
----[[
-Create a function-call dispatching target.
-
-@class function
-@name call
-@param name    Target function of local controller
-@param ...             Additional parameters passed to the function
-]]
-
----[[
-Create a template render dispatching target.
-
-@class function
-@name template
-@param name    Template to be rendered
-]]
-
----[[
-Create a CBI model dispatching target.
-
-@class function
-@name cbi
-@param model   CBI model to be rendered
-]]
-
----[[
-Create a combined dispatching target for non argv and argv requests.
-
-@class function
-@name arcombine
-@param trg1    Overview Target
-@param trg2    Detail Target
-]]
-
----[[
-Create a CBI form model dispatching target.
-
-@class function
-@name form
-@param model   CBI form model tpo be rendered
-]]
-
----[[
-Access the luci.i18n translate() api.
-
-@class  function
-@name   translate
-@param  text    Text to translate
-]]
-
----[[
-No-op function used to mark translation entries for menu labels.
-
-This function does not actually translate the given argument but
-is used by build/i18n-scan.pl to find translatable entries.
-
-@class function
-@name _
-]]
-
diff --git a/modules/luci-base/luasrc/template.lua b/modules/luci-base/luasrc/template.lua
deleted file mode 100644 (file)
index 3955bd7..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
--- Copyright 2008 Steven Barth <steven@midlink.org>
--- Licensed to the public under the Apache License 2.0.
-
-local util = require "luci.util"
-local config = require "luci.config"
-local tparser = require "luci.template.parser"
-
-local tostring, pairs, loadstring = tostring, pairs, loadstring
-local setmetatable, loadfile = setmetatable, loadfile
-local getfenv, setfenv, rawget = getfenv, setfenv, rawget
-local assert, type, error = assert, type, error
-
---- LuCI template library.
-module "luci.template"
-
-config.template = config.template or {}
-viewdir = config.template.viewdir or util.libpath() .. "/view"
-
-
--- Define the namespace for template modules
-context = util.threadlocal()
-
---- Render a certain template.
--- @param name         Template name
--- @param scope                Scope to assign to template (optional)
-function render(name, scope)
-       return Template(name):render(scope or getfenv(2))
-end
-
---- Render a template from a string.
--- @param template     Template string
--- @param scope                Scope to assign to template (optional)
-function render_string(template, scope)
-       return Template(nil, template):render(scope or getfenv(2))
-end
-
-
--- Template class
-Template = util.class()
-
--- Shared template cache to store templates in to avoid unnecessary reloading
-Template.cache = setmetatable({}, {__mode = "v"})
-
-
--- Constructor - Reads and compiles the template on-demand
-function Template.__init__(self, name, template)
-       if name then
-               self.template = self.cache[name]
-               self.name = name
-       else
-               self.name = "[string]"
-       end
-
-       -- Create a new namespace for this template
-       self.viewns = context.viewns
-
-       -- If we have a cached template, skip compiling and loading
-       if not self.template then
-
-               -- Compile template
-               local err
-               local sourcefile
-
-               if name then
-                       sourcefile = viewdir .. "/" .. name .. ".htm"
-                       self.template, _, err = tparser.parse(sourcefile)
-               else
-                       sourcefile = "[string]"
-                       self.template, _, err = tparser.parse_string(template)
-               end
-
-               -- If we have no valid template throw error, otherwise cache the template
-               if not self.template then
-                       error("Failed to load template '" .. self.name .. "'.\n" ..
-                             "Error while parsing template '" .. sourcefile .. "':\n" ..
-                             (err or "Unknown syntax error"))
-               elseif name then
-                       self.cache[name] = self.template
-               end
-       end
-end
-
-
--- Renders a template
-function Template.render(self, scope)
-       scope = scope or getfenv(2)
-
-       -- Put our predefined objects in the scope of the template
-       setfenv(self.template, setmetatable({}, {__index =
-               function(tbl, key)
-                       return rawget(tbl, key) or self.viewns[key] or scope[key]
-               end}))
-
-       -- Now finally render the thing
-       local stat, err = util.copcall(self.template)
-       if not stat then
-               error("Failed to execute template '" .. self.name .. "'.\n" ..
-                     "A runtime error occurred: " .. tostring(err or "(nil)"))
-       end
-end
diff --git a/modules/luci-base/luasrc/view/csrftoken.htm b/modules/luci-base/luasrc/view/csrftoken.htm
deleted file mode 100644 (file)
index 57ac03f..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<%#
- Copyright 2015 Jo-Philipp Wich <jow@openwrt.org>
- Licensed to the public under the Apache License 2.0.
--%>
-
-<%+header%>
-
-<h2 name="content"><%:Form token mismatch%></h2>
-<br />
-
-<p class="alert-message"><%:The submitted security token is invalid or already expired!%></p>
-
-<p><%:
-       In order to prevent unauthorized access to the system, your request has
-       been blocked. Click "Continue Â»" below to return to the previous page.
-%></p>
-
-<hr />
-
-<p class="right">
-       <strong><a href="#" onclick="window.history.back();">Continue Â»</a></strong>
-</p>
-
-<%+footer%>
diff --git a/modules/luci-base/luasrc/view/error404.htm b/modules/luci-base/luasrc/view/error404.htm
deleted file mode 100644 (file)
index ff151d1..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<%#
- Copyright 2008 Steven Barth <steven@midlink.org>
- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
- Licensed to the public under the Apache License 2.0.
--%>
-
-<%+header%>
-<h2 name="content">404 <%:Not Found%></h2>
-<p><%:Sorry, the object you requested was not found.%></p>
-<p><%=message%></p>
-<tt><%:Unable to dispatch%>: <%=url(unpack(luci.dispatcher.context.request))%></tt>
-<%+footer%>
diff --git a/modules/luci-base/luasrc/view/error500.htm b/modules/luci-base/luasrc/view/error500.htm
deleted file mode 100644 (file)
index 34a52cd..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<%#
- Copyright 2008 Steven Barth <steven@midlink.org>
- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
- Licensed to the public under the Apache License 2.0.
--%>
-
-<%+header%>
-<h2 name="content">500 <%:Internal Server Error%></h2>
-<p><%:Sorry, the server encountered an unexpected error.%></p>
-<pre class="error500"><%=message%></pre>
-<%+footer%>
diff --git a/modules/luci-base/luasrc/view/footer.htm b/modules/luci-base/luasrc/view/footer.htm
deleted file mode 100644 (file)
index ba14ec8..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<%#
- Copyright 2008 Steven Barth <steven@midlink.org>
- Copyright 2008-2019 Jo-Philipp Wich <jo@mein.io>
- Licensed to the public under the Apache License 2.0.
--%>
-
-<%
-       local is_rollback_pending, rollback_time_remaining, rollback_session, rollback_token = luci.model.uci:rollback_pending()
-
-       if is_rollback_pending or trigger_apply or trigger_revert then
-%>
-       <script type="text/javascript">
-               document.addEventListener("luci-loaded", function() {
-                       <% if trigger_apply then -%>
-                               L.ui.changes.apply(true);
-                       <%- elseif trigger_revert then -%>
-                               L.ui.changes.revert();
-                       <%- else -%>
-                               L.ui.changes.confirm(true, Date.now() + <%=rollback_time_remaining%> * 1000, <%=luci.http.write_json(rollback_token)%>);
-                       <%- end %>
-               });
-       </script>
-<%
-       end
-
-       include("themes/" .. theme .. "/footer")
-%>
diff --git a/modules/luci-base/luasrc/view/header.htm b/modules/luci-base/luasrc/view/header.htm
deleted file mode 100644 (file)
index cffe948..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-<%#
- Copyright 2008 Steven Barth <steven@midlink.org>
- Copyright 2008-2019 Jo-Philipp Wich <jo@mein.io>
- Licensed to the public under the Apache License 2.0.
--%>
-
-<%
-       if not luci.dispatcher.context.template_header_sent then
-               include("themes/" .. theme .. "/header")
-               luci.dispatcher.context.template_header_sent = true
-       end
-
-       local applyconf = luci.config and luci.config.apply
-%>
-
-<script type="text/javascript" src="<%=resource%>/promis.min.js"></script>
-<script type="text/javascript" src="<%=resource%>/luci.js"></script>
-<script type="text/javascript">
-       L = new LuCI(<%= luci.http.write_json({
-               token          = token,
-               media          = media,
-               resource       = resource,
-               scriptname     = luci.http.getenv("SCRIPT_NAME"),
-               pathinfo       = luci.http.getenv("PATH_INFO"),
-               documentroot   = luci.http.getenv("DOCUMENT_ROOT"),
-               requestpath    = luci.dispatcher.context.requestpath,
-               dispatchpath   = luci.dispatcher.context.path,
-               pollinterval   = luci.config.main.pollinterval or 5,
-               ubuspath       = luci.config.main.ubuspath or '/ubus/',
-               sessionid      = luci.dispatcher.context.authsession,
-               nodespec       = luci.dispatcher.context.dispatched,
-               apply_rollback = math.max(applyconf and applyconf.rollback or 90, 90),
-               apply_holdoff  = math.max(applyconf and applyconf.holdoff or 4, 1),
-               apply_timeout  = math.max(applyconf and applyconf.timeout or 5, 1),
-               apply_display  = math.max(applyconf and applyconf.display or 1.5, 1),
-               rollback_token = rollback_token
-       }) %>);
-</script>
diff --git a/modules/luci-base/luasrc/view/sysauth.htm b/modules/luci-base/luasrc/view/sysauth.htm
deleted file mode 100644 (file)
index 797c87a..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-<%#
- Copyright 2008 Steven Barth <steven@midlink.org>
- Copyright 2008-2012 Jo-Philipp Wich <jow@openwrt.org>
- Licensed to the public under the Apache License 2.0.
--%>
-
-<%+header%>
-
-<form method="post" action="<%=pcdata(FULL_REQUEST_URI)%>">
-       <%- if fuser then %>
-               <div class="alert-message warning">
-                       <p><%:Invalid username and/or password! Please try again.%></p>
-               </div>
-       <% end -%>
-
-       <div class="cbi-map">
-               <h2 name="content"><%:Authorization Required%></h2>
-               <div class="cbi-map-descr">
-                       <%:Please enter your username and password.%>
-               </div>
-               <div class="cbi-section"><div class="cbi-section-node">
-                       <div class="cbi-value">
-                               <label class="cbi-value-title" for="luci_username"><%:Username%></label>
-                               <div class="cbi-value-field">
-                                       <input class="cbi-input-text" type="text" name="luci_username" id="luci_username" autocomplete="username" value="<%=duser%>" />
-                               </div>
-                       </div>
-                       <div class="cbi-value cbi-value-last">
-                               <label class="cbi-value-title" for="luci_password"><%:Password%></label>
-                               <div class="cbi-value-field">
-                                       <input class="cbi-input-text" type="password" name="luci_password" id="luci_password" autocomplete="current-password"/>
-                               </div>
-                       </div>
-               </div></div>
-       </div>
-
-       <div class="cbi-page-actions">
-               <input type="submit" value="<%:Login%>" class="btn cbi-button cbi-button-apply" />
-               <input type="reset" value="<%:Reset%>" class="btn cbi-button cbi-button-reset" />
-       </div>
-</form>
-<script type="text/javascript">//<![CDATA[
-       var input = document.getElementsByName('luci_password')[0];
-       if (input)
-               input.focus();
-//]]></script>
-
-<%
-local uci  = require "luci.model.uci".cursor()
-local fs  = require "nixio.fs"
-local https_key = uci:get("uhttpd", "main", "key")
-local https_port = uci:get("uhttpd", "main", "listen_https")
-if type(https_port) == "table" then
-       https_port = https_port[1]
-end
-
-if https_port and fs.access(https_key) then
-       https_port = https_port:match("(%d+)$")
-%>
-
-<script type="text/javascript">//<![CDATA[
-       if (document.location.protocol != 'https:') {
-               var url = 'https://' + window.location.hostname + ':' + '<%=https_port%>' + window.location.pathname;
-               var img=new Image;
-               img.onload=function(){window.location = url};
-               img.src='https://' + window.location.hostname + ':' + '<%=https_port%>' + '<%=resource%>/icons/loading.gif?' + Math.random();
-               setTimeout(function(){
-                       img.src=''
-               }, 5000);
-       }
-//]]></script>
-
-<% end %>
-
-<%+footer%>
diff --git a/modules/luci-base/luasrc/view/view.htm b/modules/luci-base/luasrc/view/view.htm
deleted file mode 100644 (file)
index b451e8c..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<%+header%>
-
-<div id="view">
-       <div class="spinning"><%:Loading view…%></div>
-       <script type="text/javascript">
-               L.require('ui').then(function(ui) {
-                       ui.instantiateView('<%=view%>');
-               });
-       </script>
-</div>
-
-<%+footer%>
diff --git a/modules/luci-base/root/usr/libexec/rpcd/luci b/modules/luci-base/root/usr/libexec/rpcd/luci
deleted file mode 100755 (executable)
index f124512..0000000
+++ /dev/null
@@ -1,682 +0,0 @@
-#!/usr/bin/env lua
-
-local json = require "luci.jsonc"
-local fs   = require "nixio.fs"
-
-local function readfile(path)
-       local s = fs.readfile(path)
-       return s and (s:gsub("^%s+", ""):gsub("%s+$", ""))
-end
-
-local methods = {
-       getInitList = {
-               args = { name = "name" },
-               call = function(args)
-                       local sys = require "luci.sys"
-                       local _, name, scripts = nil, nil, {}
-                       for _, name in ipairs(args.name and { args.name } or sys.init.names()) do
-                               local index = sys.init.index(name)
-                               if index then
-                                       scripts[name] = { index = index, enabled = sys.init.enabled(name) }
-                               else
-                                       return { error = "No such init script" }
-                               end
-                       end
-                       return scripts
-               end
-       },
-
-       setInitAction = {
-               args = { name = "name", action = "action" },
-               call = function(args)
-                       local sys = require "luci.sys"
-                       if type(sys.init[args.action]) ~= "function" then
-                               return { error = "Invalid action" }
-                       end
-                       return { result = sys.init[args.action](args.name) }
-               end
-       },
-
-       getLocaltime = {
-               call = function(args)
-                       return { result = os.time() }
-               end
-       },
-
-       setLocaltime = {
-               args = { localtime = 0 },
-               call = function(args)
-                       local sys = require "luci.sys"
-                       local date = os.date("*t", args.localtime)
-                       if date then
-                               sys.call("date -s '%04d-%02d-%02d %02d:%02d:%02d' >/dev/null" %{ date.year, date.month, date.day, date.hour, date.min, date.sec })
-                               sys.call("/etc/init.d/sysfixtime restart >/dev/null")
-                       end
-                       return { result = args.localtime }
-               end
-       },
-
-       getTimezones = {
-               call = function(args)
-                       local util  = require "luci.util"
-                       local zones = require "luci.sys.zoneinfo"
-
-                       local tz = readfile("/etc/TZ")
-                       local res = util.ubus("uci", "get", {
-                               config = "system",
-                               section = "@system[0]",
-                               option = "zonename"
-                       })
-
-                       local result = {}
-                       local _, zone
-                       for _, zone in ipairs(zones.TZ) do
-                               result[zone[1]] = {
-                                       tzstring = zone[2],
-                                       active = (res and res.value == zone[1]) and true or nil
-                               }
-                       end
-                       return result
-               end
-       },
-
-       getLEDs = {
-               call = function()
-                       local iter   = fs.dir("/sys/class/leds")
-                       local result = { }
-
-                       if iter then
-                               local led
-                               for led in iter do
-                                       local m, s
-
-                                       result[led] = { triggers = {} }
-
-                                       s = readfile("/sys/class/leds/"..led.."/trigger")
-                                       for s in (s or ""):gmatch("%S+") do
-                                               m = s:match("^%[(.+)%]$")
-                                               result[led].triggers[#result[led].triggers+1] = m or s
-                                               result[led].active_trigger = m or result[led].active_trigger
-                                       end
-
-                                       s = readfile("/sys/class/leds/"..led.."/brightness")
-                                       if s then
-                                               result[led].brightness = tonumber(s)
-                                       end
-
-                                       s = readfile("/sys/class/leds/"..led.."/max_brightness")
-                                       if s then
-                                               result[led].max_brightness = tonumber(s)
-                                       end
-                               end
-                       end
-
-                       return result
-               end
-       },
-
-       getUSBDevices = {
-               call = function()
-                       local fs     = require "nixio.fs"
-                       local iter   = fs.glob("/sys/bus/usb/devices/[0-9]*/manufacturer")
-                       local result = { }
-
-                       if iter then
-                               result.devices = {}
-
-                               local p
-                               for p in iter do
-                                       local id = p:match("/([^/]+)/manufacturer$")
-
-                                       result.devices[#result.devices+1] = {
-                                               id      = id,
-                                               vid     = readfile("/sys/bus/usb/devices/"..id.."/idVendor"),
-                                               pid     = readfile("/sys/bus/usb/devices/"..id.."/idProduct"),
-                                               vendor  = readfile("/sys/bus/usb/devices/"..id.."/manufacturer"),
-                                               product = readfile("/sys/bus/usb/devices/"..id.."/product"),
-                                               speed   = tonumber((readfile("/sys/bus/usb/devices/"..id.."/product")))
-                                       }
-                               end
-                       end
-
-                       iter = fs.glob("/sys/bus/usb/devices/*/*-port[0-9]*")
-
-                       if iter then
-                               result.ports = {}
-
-                               local p
-                               for p in iter do
-                                       local port = p:match("([^/]+)$")
-                                       local link = fs.readlink(p.."/device")
-
-                                       result.ports[#result.ports+1] = {
-                                               port   = port,
-                                               device = link and fs.basename(link)
-                                       }
-                               end
-                       end
-
-                       return result
-               end
-       },
-
-       getConntrackHelpers = {
-               call = function()
-                       local ok, fd = pcall(io.open, "/usr/share/fw3/helpers.conf", "r")
-                       local rv = {}
-
-                       if not (ok and fd) then
-                               ok, fd = pcall(io.open, "/usr/share/firewall4/helpers", "r")
-                       end
-
-                       if ok and fd then
-                               local entry
-
-                               while true do
-                                       local line = fd:read("*l")
-                                       if not line then
-                                               break
-                                       end
-
-                                       if line:match("^%s*config%s") then
-                                               if entry then
-                                                       rv[#rv+1] = entry
-                                               end
-                                               entry = {}
-                                       else
-                                               local opt, val = line:match("^%s*option%s+(%S+)%s+(%S.*)$")
-                                               if opt and val then
-                                                       opt = opt:gsub("^'(.+)'$", "%1"):gsub('^"(.+)"$', "%1")
-                                                       val = val:gsub("^'(.+)'$", "%1"):gsub('^"(.+)"$', "%1")
-                                                       entry[opt] = val
-                                               end
-                                       end
-                               end
-
-                               if entry then
-                                       rv[#rv+1] = entry
-                               end
-
-                               fd:close()
-                       end
-
-                       return { result = rv }
-               end
-       },
-
-       getFeatures = {
-               call = function()
-                       local fs = require "nixio.fs"
-                       local rv = {}
-                       local ok, fd
-
-                       rv.firewall      = fs.access("/sbin/fw3")
-                       rv.firewall4     = fs.access("/sbin/fw4")
-                       rv.opkg          = fs.access("/bin/opkg")
-                       rv.offloading    = fs.access("/sys/module/xt_FLOWOFFLOAD/refcnt") or fs.access("/sys/module/nft_flow_offload/refcnt")
-                       rv.br2684ctl     = fs.access("/usr/sbin/br2684ctl")
-                       rv.swconfig      = fs.access("/sbin/swconfig")
-                       rv.odhcpd        = fs.access("/usr/sbin/odhcpd")
-                       rv.zram          = fs.access("/sys/class/zram-control")
-                       rv.sysntpd       = fs.readlink("/usr/sbin/ntpd") and true
-                       rv.ipv6          = fs.access("/proc/net/ipv6_route")
-                       rv.dropbear      = fs.access("/usr/sbin/dropbear")
-                       rv.cabundle      = fs.access("/etc/ssl/certs/ca-certificates.crt")
-                       rv.relayd        = fs.access("/usr/sbin/relayd")
-
-                       local wifi_features = { "eap", "11n", "11ac", "11r", "acs", "sae", "owe", "suiteb192", "wep", "wps" }
-
-                       if fs.access("/usr/sbin/hostapd") then
-                               rv.hostapd = { cli = fs.access("/usr/sbin/hostapd_cli") }
-
-                               local _, feature
-                               for _, feature in ipairs(wifi_features) do
-                                       rv.hostapd[feature] =
-                                               (os.execute(string.format("/usr/sbin/hostapd -v%s >/dev/null 2>/dev/null", feature)) == 0)
-                               end
-                       end
-
-                       if fs.access("/usr/sbin/wpa_supplicant") then
-                               rv.wpasupplicant = { cli = fs.access("/usr/sbin/wpa_cli") }
-
-                               local _, feature
-                               for _, feature in ipairs(wifi_features) do
-                                       rv.wpasupplicant[feature] =
-                                               (os.execute(string.format("/usr/sbin/wpa_supplicant -v%s >/dev/null 2>/dev/null", feature)) == 0)
-                               end
-                       end
-
-                       ok, fd = pcall(io.popen, "dnsmasq --version 2>/dev/null")
-                       if ok then
-                               rv.dnsmasq = {}
-
-                               while true do
-                                       local line = fd:read("*l")
-                                       if not line then
-                                               break
-                                       end
-
-                                       local opts = line:match("^Compile time options: (.+)$")
-                                       if opts then
-                                               local opt
-                                               for opt in opts:gmatch("%S+") do
-                                                       local no = opt:match("^no%-(%S+)$")
-                                                       rv.dnsmasq[string.lower(no or opt)] = not no
-                                               end
-                                               break
-                                       end
-                               end
-
-                               fd:close()
-                       end
-
-                       ok, fd = pcall(io.popen, "ipset --help 2>/dev/null")
-                       if ok then
-                               rv.ipset = {}
-
-                               local sets = false
-
-                               while true do
-                                       local line = fd:read("*l")
-                                       if not line then
-                                               break
-                                       elseif line:match("^Supported set types:") then
-                                               sets = true
-                                       elseif sets then
-                                               local set, ver = line:match("^%s+(%S+)%s+(%d+)")
-                                               if set and not rv.ipset[set] then
-                                                       rv.ipset[set] = tonumber(ver)
-                                               end
-                                       end
-                               end
-
-                               fd:close()
-                       end
-
-                       return rv
-               end
-       },
-
-       getSwconfigFeatures = {
-               args = { switch = "switch0" },
-               call = function(args)
-                       local util = require "luci.util"
-
-                       -- Parse some common switch properties from swconfig help output.
-                       local swc, err = io.popen("swconfig dev %s help 2>/dev/null" % util.shellquote(args.switch))
-                       if swc then
-                               local is_port_attr = false
-                               local is_vlan_attr = false
-                               local rv = {}
-
-                               while true do
-                                       local line = swc:read("*l")
-                                       if not line then break end
-
-                                       if line:match("^%s+%-%-vlan") then
-                                               is_vlan_attr = true
-
-                                       elseif line:match("^%s+%-%-port") then
-                                               is_vlan_attr = false
-                                               is_port_attr = true
-
-                                       elseif line:match("cpu @") then
-                                               rv.switch_title = line:match("^switch%d: %w+%((.-)%)")
-                                               rv.num_vlans    = tonumber(line:match("vlans: (%d+)")) or 16
-                                               rv.min_vid      = 1
-
-                                       elseif line:match(": pvid") or line:match(": tag") or line:match(": vid") then
-                                               if is_vlan_attr then rv.vid_option = line:match(": (%w+)") end
-
-                                       elseif line:match(": enable_vlan4k") then
-                                               rv.vlan4k_option = "enable_vlan4k"
-
-                                       elseif line:match(": enable_vlan") then
-                                               rv.vlan_option = "enable_vlan"
-
-                                       elseif line:match(": enable_learning") then
-                                               rv.learning_option = "enable_learning"
-
-                                       elseif line:match(": enable_mirror_rx") then
-                                               rv.mirror_option = "enable_mirror_rx"
-
-                                       elseif line:match(": max_length") then
-                                               rv.jumbo_option = "max_length"
-                                       end
-                               end
-
-                               swc:close()
-
-                               if not next(rv) then
-                                       return { error = "No such switch" }
-                               end
-
-                               return rv
-                       else
-                               return { error = err }
-                       end
-               end
-       },
-
-       getSwconfigPortState = {
-               args = { switch = "switch0" },
-               call = function(args)
-                       local util = require "luci.util"
-
-                       local swc, err = io.popen("swconfig dev %s show 2>/dev/null" % util.shellquote(args.switch))
-                       if swc then
-                               local ports = { }
-
-                               while true do
-                                       local line = swc:read("*l")
-                                       if not line or (line:match("^VLAN %d+:") and #ports > 0) then
-                                               break
-                                       end
-
-                                       local pnum = line:match("^Port (%d+):$")
-                                       if pnum then
-                                               port = {
-                                                       port = tonumber(pnum),
-                                                       duplex = false,
-                                                       speed = 0,
-                                                       link = false,
-                                                       auto = false,
-                                                       rxflow = false,
-                                                       txflow = false
-                                               }
-
-                                               ports[#ports+1] = port
-                                       end
-
-                                       if port then
-                                               local m
-
-                                               if line:match("full[%- ]duplex") then
-                                                       port.duplex = true
-                                               end
-
-                                               m = line:match(" speed:(%d+)")
-                                               if m then
-                                                       port.speed = tonumber(m)
-                                               end
-
-                                               m = line:match("(%d+) Mbps")
-                                               if m and port.speed == 0 then
-                                                       port.speed = tonumber(m)
-                                               end
-
-                                               m = line:match("link: (%d+)")
-                                               if m and port.speed == 0 then
-                                                       port.speed = tonumber(m)
-                                               end
-
-                                               if line:match("link: ?up") or line:match("status: ?up") then
-                                                       port.link = true
-                                               end
-
-                                               if line:match("auto%-negotiate") or line:match("link:.-auto") then
-                                                       port.auto = true
-                                               end
-
-                                               if line:match("link:.-rxflow") then
-                                                       port.rxflow = true
-                                               end
-
-                                               if line:match("link:.-txflow") then
-                                                       port.txflow = true
-                                               end
-                                       end
-                               end
-
-                               swc:close()
-
-                               if not next(ports) then
-                                       return { error = "No such switch" }
-                               end
-
-                               return { result = ports }
-                       else
-                               return { error = err }
-                       end
-               end
-       },
-
-       setPassword = {
-               args = { username = "root", password = "password" },
-               call = function(args)
-                       local util = require "luci.util"
-                       return {
-                               result = (os.execute("(echo %s; sleep 1; echo %s) | /bin/busybox passwd %s >/dev/null 2>&1" %{
-                                       luci.util.shellquote(args.password),
-                                       luci.util.shellquote(args.password),
-                                       luci.util.shellquote(args.username)
-                               }) == 0)
-                       }
-               end
-       },
-
-       getBlockDevices = {
-               call = function()
-                       local fs = require "nixio.fs"
-
-                       local block = io.popen("/sbin/block info", "r")
-                       if block then
-                               local rv = {}
-
-                               while true do
-                                       local ln = block:read("*l")
-                                       if not ln then
-                                               break
-                                       end
-
-                                       local dev = ln:match("^/dev/(.-):")
-                                       if dev then
-                                               local s = tonumber((fs.readfile("/sys/class/block/" .. dev .."/size")))
-                                               local e = {
-                                                       dev = "/dev/" .. dev,
-                                                       size = s and s * 512
-                                               }
-
-                                               local key, val = { }
-                                               for key, val in ln:gmatch([[(%w+)="(.-)"]]) do
-                                                       e[key:lower()] = val
-                                               end
-
-                                               rv[dev] = e
-                                       end
-                               end
-
-                               block:close()
-
-                               return rv
-                       else
-                               return { error = "Unable to execute block utility" }
-                       end
-               end
-       },
-
-       setBlockDetect = {
-               call = function()
-                       return { result = (os.execute("/sbin/block detect > /etc/config/fstab") == 0) }
-               end
-       },
-
-       getMountPoints = {
-               call = function()
-                       local fs = require "nixio.fs"
-
-                       local fd, err = io.open("/proc/mounts", "r")
-                       if fd then
-                               local rv = {}
-
-                               while true do
-                                       local ln = fd:read("*l")
-                                       if not ln then
-                                               break
-                                       end
-
-                                       local device, mount, fstype, options, freq, pass = ln:match("^(%S*) (%S*) (%S*) (%S*) (%d+) (%d+)$")
-                                       if device and mount then
-                                               device = device:gsub("\\(%d+)", function(n) return string.char(tonumber(n, 8)) end)
-                                               mount = mount:gsub("\\(%d+)", function(n) return string.char(tonumber(n, 8)) end)
-
-                                               local stat = fs.statvfs(mount)
-                                               if stat and stat.blocks > 0 then
-                                                       rv[#rv+1] = {
-                                                               device = device,
-                                                               mount  = mount,
-                                                               size   = stat.bsize * stat.blocks,
-                                                               avail  = stat.bsize * stat.bavail,
-                                                               free   = stat.bsize * stat.bfree
-                                                       }
-                                               end
-                                       end
-                               end
-
-                               fd:close()
-
-                               return { result = rv }
-                       else
-                               return { error = err }
-                       end
-               end
-       },
-
-       getRealtimeStats = {
-               args = { mode = "interface", device = "eth0" },
-               call = function(args)
-                       local util = require "luci.util"
-
-                       local flags
-                       if args.mode == "interface" then
-                               flags = "-i %s" % util.shellquote(args.device)
-                       elseif args.mode == "wireless" then
-                               flags = "-r %s" % util.shellquote(args.device)
-                       elseif args.mode == "conntrack" then
-                               flags = "-c"
-                       elseif args.mode == "load" then
-                               flags = "-l"
-                       else
-                               return { error = "Invalid mode" }
-                       end
-
-                       local fd, err = io.popen("luci-bwc %s" % flags, "r")
-                       if fd then
-                               local parse = json.new()
-                               local done
-
-                               parse:parse("[")
-
-                               while true do
-                                       local ln = fd:read("*l")
-                                       if not ln then
-                                               break
-                                       end
-
-                                       done, err = parse:parse((ln:gsub("%d+", "%1.0")))
-
-                                       if done then
-                                               err = "Unexpected JSON data"
-                                       end
-
-                                       if err then
-                                               break
-                                       end
-                               end
-
-                               fd:close()
-
-                               done, err = parse:parse("]")
-
-                               if err then
-                                       return { error = err }
-                               elseif not done then
-                                       return { error = "Incomplete JSON data" }
-                               else
-                                       return { result = parse:get() }
-                               end
-                       else
-                               return { error = err }
-                       end
-               end
-       },
-
-       getConntrackList = {
-               call = function()
-                       local sys = require "luci.sys"
-                       return { result = sys.net.conntrack() }
-               end
-       },
-
-       getProcessList = {
-               call = function()
-                       local sys = require "luci.sys"
-                       local res = {}
-                       for _, v in pairs(sys.process.list()) do
-                               res[#res + 1] = v
-                       end
-                       return { result = res }
-               end
-       }
-}
-
-local function parseInput()
-       local parse = json.new()
-       local done, err
-
-       while true do
-               local chunk = io.read(4096)
-               if not chunk then
-                       break
-               elseif not done and not err then
-                       done, err = parse:parse(chunk)
-               end
-       end
-
-       if not done then
-               print(json.stringify({ error = err or "Incomplete input" }))
-               os.exit(1)
-       end
-
-       return parse:get()
-end
-
-local function validateArgs(func, uargs)
-       local method = methods[func]
-       if not method then
-               print(json.stringify({ error = "Method not found" }))
-               os.exit(1)
-       end
-
-       if type(uargs) ~= "table" then
-               print(json.stringify({ error = "Invalid arguments" }))
-               os.exit(1)
-       end
-
-       uargs.ubus_rpc_session = nil
-
-       local k, v
-       local margs = method.args or {}
-       for k, v in pairs(uargs) do
-               if margs[k] == nil or
-                  (v ~= nil and type(v) ~= type(margs[k]))
-               then
-                       print(json.stringify({ error = "Invalid arguments" }))
-                       os.exit(1)
-               end
-       end
-
-       return method
-end
-
-if arg[1] == "list" then
-       local _, method, rv = nil, nil, {}
-       for _, method in pairs(methods) do rv[_] = method.args or {} end
-       print((json.stringify(rv):gsub(":%[%]", ":{}")))
-elseif arg[1] == "call" then
-       local args = parseInput()
-       local method = validateArgs(arg[2], args)
-       local result, code = method.call(args)
-       print((json.stringify(result):gsub("^%[%]$", "{}")))
-       os.exit(code or 0)
-end
index 605c7ab77723c0215db7e4766274f08a390aa5fb..000c3681511f43c78a49e42a9a98cb51cd81330e 100644 (file)
@@ -61,7 +61,7 @@
 
        "admin/translations/*": {
                "action": {
-                       "type": "call",
+                       "type": "function",
                        "module": "luci.controller.admin.index",
                        "function": "action_translations"
                },
@@ -70,7 +70,7 @@
 
        "admin/ubus/*": {
                "action": {
-                       "type": "call",
+                       "type": "function",
                        "module": "luci.controller.admin.index",
                        "function": "action_ubus"
                },
@@ -81,7 +81,7 @@
                "title": "Logout",
                "order": 999,
                "action": {
-                       "type": "call",
+                       "type": "function",
                        "module": "luci.controller.admin.index",
                        "function": "action_logout"
                },
@@ -99,7 +99,7 @@
 
        "admin/uci/revert": {
                "action": {
-                       "type": "call",
+                       "type": "function",
                        "module": "luci.controller.admin.uci",
                        "function": "action_revert",
                        "post": true
        "admin/uci/apply_rollback": {
                "cors": true,
                "action": {
-                       "type": "call",
+                       "type": "function",
                        "module": "luci.controller.admin.uci",
                        "function": "action_apply_rollback",
                        "post": true
        "admin/uci/apply_unchecked": {
                "cors": true,
                "action": {
-                       "type": "call",
+                       "type": "function",
                        "module": "luci.controller.admin.uci",
                        "function": "action_apply_unchecked",
                        "post": true
        "admin/uci/confirm": {
                "cors": true,
                "action": {
-                       "type": "call",
+                       "type": "function",
                        "module": "luci.controller.admin.uci",
                        "function": "action_confirm"
                },
 
        "admin/menu": {
                "action": {
-                       "type": "call",
+                       "type": "function",
                        "module": "luci.controller.admin.index",
                        "function": "action_menu"
                },
index 2a425d5ab78d0c1f0ef4ac306b8f3ea413fcabc4..896aeb0a386569c596f961a0cf46ca663f67708d 100644 (file)
@@ -4,29 +4,31 @@
 contrib/lemon: contrib/lemon.c contrib/lempar.c
        cc -o contrib/lemon $<
 
-plural_formula.c: plural_formula.y contrib/lemon
+lib/plural_formula.c: lib/plural_formula.y contrib/lemon
        ./contrib/lemon -q $<
 
-template_lmo.c: plural_formula.c
+lib/lmo.c: lib/plural_formula.c
+
+core.so: lib/luci.o lib/lmo.o lib/plural_formula.o
+       $(CC) $(LDFLAGS) -shared -o $@ $^
+
+version.uc:
+       echo "export const revision = '$(LUCI_VERSION)', branch = '$(LUCI_GITBRANCH)';" > $@
 
 clean:
-       rm -f contrib/lemon po2lmo parser.so version.lua plural_formula.c plural_formula.h *.o
+       rm -f contrib/lemon lib/*.o lib/plural_formula.c lib/plural_formula.h core.so version.uc
 
 jsmin: jsmin.o
        $(CC) $(LDFLAGS) -o $@ $^
 
-po2lmo: po2lmo.o template_lmo.o plural_formula.o
+po2lmo: po2lmo.o lib/lmo.o lib/plural_formula.o
        $(CC) $(LDFLAGS) -o $@ $^
 
-parser.so: template_parser.o template_utils.o template_lmo.o template_lualib.o plural_formula.o
-       $(CC) $(LDFLAGS) -shared -o $@ $^
-
-version.lua:
-       ./mkversion.sh $@ $(LUCI_VERSION) "$(LUCI_GITBRANCH)"
-
-compile: parser.so version.lua
+compile: core.so version.uc
 
 install: compile
-       mkdir -p $(DESTDIR)/usr/lib/lua/luci/template
-       cp parser.so $(DESTDIR)/usr/lib/lua/luci/template/parser.so
-       cp version.lua $(DESTDIR)/usr/lib/lua/luci/version.lua
+       mkdir -p $(DESTDIR)/usr/lib/ucode/luci
+       cp core.so $(DESTDIR)/usr/lib/ucode/luci/core.so
+
+       mkdir -p $(DESTDIR)/usr/share/ucode/luci
+       cp version.uc $(DESTDIR)/usr/share/ucode/luci/version.uc
index 5f398c266e5c012e19294c93df8129182bc6da87..0a04e9ba17ae5c860419653dae17e5dbe7d6656d 100644 (file)
@@ -16,7 +16,7 @@
  *  limitations under the License.
  */
 
-#include "template_lmo.h"
+#include "lib/lmo.h"
 
 static void die(const char *msg)
 {
@@ -169,8 +169,11 @@ static void print_msg(struct msg *msg, FILE *out)
                        else
                                snprintf(key, sizeof(key), "%s", msg->id);
 
-                       key_id = sfh_hash(key, strlen(key));
-                       val_id = sfh_hash(msg->val[i], strlen(msg->val[i]));
+                       len = strlen(key);
+                       key_id = sfh_hash(key, len, len);
+
+                       len = strlen(msg->val[i]);
+                       val_id = sfh_hash(msg->val[i], len, len);
 
                        if (key_id != val_id) {
                                n_entries++;
similarity index 97%
rename from modules/luci-base-ucode/ucode/runtime.uc
rename to modules/luci-base/ucode/runtime.uc
index ee0756efc3ec3bdd870776dd7cbe11f59bb8fa92..a8b6812e745bdb96f99d72118503be99c7610b14 100644 (file)
@@ -47,7 +47,6 @@ const Class = {
                if (!this.L) {
                        this.L = this.env.dispatcher.load_luabridge().create();
                        this.L.set('L', proto({ write: print }, this.env));
-                       this.L.eval('package.path = "/usr/lib/lua/luci/ucodebridge/?.lua;" .. package.path');
                        this.L.invoke('require', 'luci.ucodebridge');
 
                        this.env.lua_active = true;
index d73ca070a160f822c9f2f531127d2dec6e5cd673..4b11f16419d316dd13dc698f86919328f11a7d04 100644 (file)
@@ -12,7 +12,7 @@ LUCI_TYPE:=mod
 LUCI_BASENAME:=compat
 
 LUCI_TITLE:=LuCI compatibility libraries
-LUCI_DEPENDS:=+luci-base
+LUCI_DEPENDS:=+luci-lua-runtime
 
 include ../../luci.mk
 
diff --git a/modules/luci-lua-runtime/Makefile b/modules/luci-lua-runtime/Makefile
new file mode 100644 (file)
index 0000000..2c6f38f
--- /dev/null
@@ -0,0 +1,27 @@
+#
+# Copyright (C) 2022 Jo-Philipp Wich <jo@mein.io>
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=luci-lua-runtime
+
+LUCI_TYPE:=mod
+LUCI_BASENAME:=lua-runtime
+
+LUCI_TITLE:=LuCI Lua runtime libraries
+LUCI_DEPENDS:= \
+       +luci-base \
+       +lua \
+       +luci-lib-nixio \
+       +luci-lib-ip \
+       +luci-lib-jsonc \
+       +libubus-lua \
+       +liblucihttp-lua \
+       +ucode-mod-lua
+
+include ../../luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/modules/luci-lua-runtime/src/Makefile b/modules/luci-lua-runtime/src/Makefile
new file mode 100644 (file)
index 0000000..dcce33d
--- /dev/null
@@ -0,0 +1,26 @@
+%.o: %.c
+       $(CC) $(CPPFLAGS) $(CFLAGS) $(FPIC) -DNDEBUG -c -o $@ $<
+
+contrib/lemon: contrib/lemon.c contrib/lempar.c
+       cc -o contrib/lemon $<
+
+plural_formula.c: plural_formula.y contrib/lemon
+       ./contrib/lemon -q $<
+
+template_lmo.c: plural_formula.c
+
+clean:
+       rm -f contrib/lemon parser.so plural_formula.c plural_formula.h *.o
+
+parser.so: template_parser.o template_utils.o template_lmo.o template_lualib.o plural_formula.o
+       $(CC) $(LDFLAGS) -shared -o $@ $^
+
+version.lua:
+       ./mkversion.sh $@ $(LUCI_VERSION) "$(LUCI_GITBRANCH)"
+
+compile: parser.so version.lua
+
+install: compile
+       mkdir -p $(DESTDIR)/usr/lib/lua/luci/template
+       cp parser.so $(DESTDIR)/usr/lib/lua/luci/template/parser.so
+       cp version.lua $(DESTDIR)/usr/lib/lua/luci/version.lua
git clone https://git.99rst.org/PROJECT