treewide: separate Lua runtime resources
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 <jo@mein.io>
This commit is contained in:
parent
ded8ccf93e
commit
673f38246a
86 changed files with 189 additions and 4580 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -10,7 +10,10 @@ package-lock.json
|
||||||
modules/luci-base/src/po2lmo
|
modules/luci-base/src/po2lmo
|
||||||
modules/luci-base/src/jsmin
|
modules/luci-base/src/jsmin
|
||||||
modules/luci-base/src/contrib/lemon
|
modules/luci-base/src/contrib/lemon
|
||||||
modules/luci-base/src/plural_formula.c
|
modules/luci-base/src/ucode/plural_formula.c
|
||||||
modules/luci-base/src/plural_formula.h
|
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/*
|
||||||
!docs/jsapi/README.md
|
!docs/jsapi/README.md
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
echo -n "Updating modules/luci-base/po/templates/base.pot ... "
|
echo -n "Updating modules/luci-base/po/templates/base.pot ... "
|
||||||
|
|
||||||
./build/i18n-scan.pl \
|
./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/ \
|
modules/luci-mod-network modules/luci-mod-status modules/luci-mod-system/ \
|
||||||
protocols/ themes/ \
|
protocols/ themes/ \
|
||||||
> modules/luci-base/po/templates/base.pot
|
> modules/luci-base/po/templates/base.pot
|
||||||
|
|
|
@ -7,7 +7,7 @@ use strict;
|
||||||
my %TZ;
|
my %TZ;
|
||||||
|
|
||||||
my $tzdin = $ARGV[0] || "/usr/share/zoneinfo";
|
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";
|
local $/ = "\012";
|
||||||
open( ZTAB, "< $tzdin/zone.tab" ) || die "open($tzdin/zone.tab): $!";
|
open( ZTAB, "< $tzdin/zone.tab" ) || die "open($tzdin/zone.tab): $!";
|
||||||
|
|
|
@ -6,234 +6,66 @@ local util = require "luci.util"
|
||||||
local coroutine = require "coroutine"
|
local coroutine = require "coroutine"
|
||||||
local table = require "table"
|
local table = require "table"
|
||||||
local lhttp = require "lucihttp"
|
local lhttp = require "lucihttp"
|
||||||
local nixio = require "nixio"
|
|
||||||
local ltn12 = require "luci.ltn12"
|
|
||||||
|
|
||||||
local table, ipairs, pairs, type, tostring, tonumber, error =
|
local L, table, ipairs, pairs, type, error = _G.L, table, ipairs, pairs, type, error
|
||||||
table, ipairs, pairs, type, tostring, tonumber, error
|
|
||||||
|
|
||||||
module "luci.http"
|
module "luci.http"
|
||||||
|
|
||||||
HTTP_MAX_CONTENT = 1024*100 -- 100 kB maximum content size
|
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()
|
function close()
|
||||||
if not context.eoh then
|
L.http:close()
|
||||||
context.eoh = true
|
|
||||||
coroutine.yield(3)
|
|
||||||
end
|
|
||||||
|
|
||||||
if not context.closed then
|
|
||||||
context.closed = true
|
|
||||||
coroutine.yield(5)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function content()
|
function content()
|
||||||
return context.request:content()
|
return L.http:content()
|
||||||
end
|
end
|
||||||
|
|
||||||
function formvalue(name, noparse)
|
function formvalue(name, noparse)
|
||||||
return context.request:formvalue(name, noparse)
|
return L.http:formvalue(name, noparse)
|
||||||
end
|
end
|
||||||
|
|
||||||
function formvaluetable(prefix)
|
function formvaluetable(prefix)
|
||||||
return context.request:formvaluetable(prefix)
|
return L.http:formvaluetable(prefix)
|
||||||
end
|
end
|
||||||
|
|
||||||
function getcookie(name)
|
function getcookie(name)
|
||||||
return context.request:getcookie(name)
|
return L.http:getcookie(name)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- or the environment table itself.
|
-- or the environment table itself.
|
||||||
function getenv(name)
|
function getenv(name)
|
||||||
return context.request:getenv(name)
|
return L.http:getenv(name)
|
||||||
end
|
end
|
||||||
|
|
||||||
function setfilehandler(callback)
|
function setfilehandler(callback)
|
||||||
return context.request:setfilehandler(callback)
|
return L.http:setfilehandler(callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
function header(key, value)
|
function header(key, value)
|
||||||
if not context.headers then
|
L.http:header(key, value)
|
||||||
context.headers = {}
|
|
||||||
end
|
|
||||||
context.headers[key:lower()] = value
|
|
||||||
coroutine.yield(2, key, value)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function prepare_content(mime)
|
function prepare_content(mime)
|
||||||
if not context.headers or not context.headers["content-type"] then
|
L.http:prepare_content(mime)
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function source()
|
function source()
|
||||||
return context.request.input
|
return L.http.input
|
||||||
end
|
end
|
||||||
|
|
||||||
function status(code, message)
|
function status(code, message)
|
||||||
code = code or 200
|
L.http:status(code, message)
|
||||||
message = message or "OK"
|
|
||||||
context.status = code
|
|
||||||
coroutine.yield(1, code, message)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- This function is as a valid LTN12 sink.
|
-- This function is as a valid LTN12 sink.
|
||||||
-- If the content chunk is nil this function will automatically invoke close.
|
-- If the content chunk is nil this function will automatically invoke close.
|
||||||
function write(content, src_err)
|
function write(content, src_err)
|
||||||
if not content then
|
if src_err then
|
||||||
if src_err then
|
error(src_err)
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return L.print(content)
|
||||||
end
|
end
|
||||||
|
|
||||||
function splice(fd, size)
|
function splice(fd, size)
|
||||||
|
@ -241,10 +73,7 @@ function splice(fd, size)
|
||||||
end
|
end
|
||||||
|
|
||||||
function redirect(url)
|
function redirect(url)
|
||||||
if url == "" then url = "/" end
|
L.http:redirect(url)
|
||||||
status(302, "Found")
|
|
||||||
header("Location", url)
|
|
||||||
close()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function build_querystring(q)
|
function build_querystring(q)
|
||||||
|
@ -266,35 +95,7 @@ urldecode = util.urldecode
|
||||||
urlencode = util.urlencode
|
urlencode = util.urlencode
|
||||||
|
|
||||||
function write_json(x)
|
function write_json(x)
|
||||||
util.serialize_json(x, write)
|
L.printf('%J', x)
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- separated by "&". Tables are encoded as parameters with multiple values by
|
-- separated by "&". Tables are encoded as parameters with multiple values by
|
||||||
|
@ -332,223 +133,12 @@ function urlencode_params(tbl)
|
||||||
return table.concat(enc, "")
|
return table.concat(enc, "")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Content-Type. Stores all extracted data associated with its parameter name
|
context = {
|
||||||
-- in the params table within the given message object. Multiple parameter
|
request = {
|
||||||
-- values are stored as tables, ordinary ones as strings.
|
formvalue = function(self, ...) return formvalue(...) end;
|
||||||
-- If an optional file callback function is given then it is fed with the
|
formvaluetable = function(self, ...) return formvaluetable(...) end;
|
||||||
-- file contents chunk by chunk and only the extracted file name is stored
|
content = function(self, ...) return content(...) end;
|
||||||
-- within the params table. The callback function will be called subsequently
|
getcookie = function(self, ...) return getcookie(...) end;
|
||||||
-- with three arguments:
|
setfilehandler = function(self, ...) return setfilehandler(...) end;
|
||||||
-- 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
|
|
||||||
|
|
|
@ -100,32 +100,8 @@ end
|
||||||
-- Scope manipulation routines
|
-- 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)
|
function threadlocal(tbl)
|
||||||
return setmetatable(tbl or {}, tl_meta)
|
return tbl or {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -772,7 +748,6 @@ function coxpcall(f, err, ...)
|
||||||
co = coroutine.create(newf)
|
co = coroutine.create(newf)
|
||||||
end
|
end
|
||||||
coromap[co] = current
|
coromap[co] = current
|
||||||
coxpt[co] = coxpt[current] or current or 0
|
|
||||||
return performResume(err, co, ...)
|
return performResume(err, co, ...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -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
|
|
|
@ -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();
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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 .
|
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||||
#
|
#
|
||||||
|
@ -11,8 +11,20 @@ PKG_NAME:=luci-base
|
||||||
LUCI_TYPE:=mod
|
LUCI_TYPE:=mod
|
||||||
LUCI_BASENAME:=base
|
LUCI_BASENAME:=base
|
||||||
|
|
||||||
LUCI_TITLE:=LuCI core libraries
|
LUCI_TITLE:=LuCI core runtime
|
||||||
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_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
|
PKG_LICENSE:=MIT
|
||||||
|
|
||||||
|
@ -26,6 +38,20 @@ define Package/luci-base/conffiles
|
||||||
/etc/config/ucitrack
|
/etc/config/ucitrack
|
||||||
endef
|
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
|
include ../../luci.mk
|
||||||
|
|
||||||
define Host/Configure
|
define Host/Configure
|
||||||
|
|
|
@ -1,5 +1,41 @@
|
||||||
#!/usr/bin/lua
|
#!/usr/bin/env ucode
|
||||||
require "luci.cacheloader"
|
|
||||||
require "luci.sgi.cgi"
|
'use strict';
|
||||||
luci.dispatcher.indexcache = "/tmp/luci-indexcache"
|
|
||||||
luci.sgi.cgi.run()
|
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();
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
File diff suppressed because it is too large
Load diff
|
@ -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 _
|
|
||||||
]]
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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%>
|
|
|
@ -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%>
|
|
|
@ -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%>
|
|
|
@ -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")
|
|
||||||
%>
|
|
|
@ -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>
|
|
|
@ -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%>
|
|
|
@ -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%>
|
|
|
@ -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
|
|
|
@ -61,7 +61,7 @@
|
||||||
|
|
||||||
"admin/translations/*": {
|
"admin/translations/*": {
|
||||||
"action": {
|
"action": {
|
||||||
"type": "call",
|
"type": "function",
|
||||||
"module": "luci.controller.admin.index",
|
"module": "luci.controller.admin.index",
|
||||||
"function": "action_translations"
|
"function": "action_translations"
|
||||||
},
|
},
|
||||||
|
@ -70,7 +70,7 @@
|
||||||
|
|
||||||
"admin/ubus/*": {
|
"admin/ubus/*": {
|
||||||
"action": {
|
"action": {
|
||||||
"type": "call",
|
"type": "function",
|
||||||
"module": "luci.controller.admin.index",
|
"module": "luci.controller.admin.index",
|
||||||
"function": "action_ubus"
|
"function": "action_ubus"
|
||||||
},
|
},
|
||||||
|
@ -81,7 +81,7 @@
|
||||||
"title": "Logout",
|
"title": "Logout",
|
||||||
"order": 999,
|
"order": 999,
|
||||||
"action": {
|
"action": {
|
||||||
"type": "call",
|
"type": "function",
|
||||||
"module": "luci.controller.admin.index",
|
"module": "luci.controller.admin.index",
|
||||||
"function": "action_logout"
|
"function": "action_logout"
|
||||||
},
|
},
|
||||||
|
@ -99,7 +99,7 @@
|
||||||
|
|
||||||
"admin/uci/revert": {
|
"admin/uci/revert": {
|
||||||
"action": {
|
"action": {
|
||||||
"type": "call",
|
"type": "function",
|
||||||
"module": "luci.controller.admin.uci",
|
"module": "luci.controller.admin.uci",
|
||||||
"function": "action_revert",
|
"function": "action_revert",
|
||||||
"post": true
|
"post": true
|
||||||
|
@ -109,7 +109,7 @@
|
||||||
"admin/uci/apply_rollback": {
|
"admin/uci/apply_rollback": {
|
||||||
"cors": true,
|
"cors": true,
|
||||||
"action": {
|
"action": {
|
||||||
"type": "call",
|
"type": "function",
|
||||||
"module": "luci.controller.admin.uci",
|
"module": "luci.controller.admin.uci",
|
||||||
"function": "action_apply_rollback",
|
"function": "action_apply_rollback",
|
||||||
"post": true
|
"post": true
|
||||||
|
@ -122,7 +122,7 @@
|
||||||
"admin/uci/apply_unchecked": {
|
"admin/uci/apply_unchecked": {
|
||||||
"cors": true,
|
"cors": true,
|
||||||
"action": {
|
"action": {
|
||||||
"type": "call",
|
"type": "function",
|
||||||
"module": "luci.controller.admin.uci",
|
"module": "luci.controller.admin.uci",
|
||||||
"function": "action_apply_unchecked",
|
"function": "action_apply_unchecked",
|
||||||
"post": true
|
"post": true
|
||||||
|
@ -135,7 +135,7 @@
|
||||||
"admin/uci/confirm": {
|
"admin/uci/confirm": {
|
||||||
"cors": true,
|
"cors": true,
|
||||||
"action": {
|
"action": {
|
||||||
"type": "call",
|
"type": "function",
|
||||||
"module": "luci.controller.admin.uci",
|
"module": "luci.controller.admin.uci",
|
||||||
"function": "action_confirm"
|
"function": "action_confirm"
|
||||||
},
|
},
|
||||||
|
@ -144,7 +144,7 @@
|
||||||
|
|
||||||
"admin/menu": {
|
"admin/menu": {
|
||||||
"action": {
|
"action": {
|
||||||
"type": "call",
|
"type": "function",
|
||||||
"module": "luci.controller.admin.index",
|
"module": "luci.controller.admin.index",
|
||||||
"function": "action_menu"
|
"function": "action_menu"
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,29 +4,31 @@
|
||||||
contrib/lemon: contrib/lemon.c contrib/lempar.c
|
contrib/lemon: contrib/lemon.c contrib/lempar.c
|
||||||
cc -o contrib/lemon $<
|
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 $<
|
./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:
|
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
|
jsmin: jsmin.o
|
||||||
$(CC) $(LDFLAGS) -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 $@ $^
|
$(CC) $(LDFLAGS) -o $@ $^
|
||||||
|
|
||||||
parser.so: template_parser.o template_utils.o template_lmo.o template_lualib.o plural_formula.o
|
compile: core.so version.uc
|
||||||
$(CC) $(LDFLAGS) -shared -o $@ $^
|
|
||||||
|
|
||||||
version.lua:
|
|
||||||
./mkversion.sh $@ $(LUCI_VERSION) "$(LUCI_GITBRANCH)"
|
|
||||||
|
|
||||||
compile: parser.so version.lua
|
|
||||||
|
|
||||||
install: compile
|
install: compile
|
||||||
mkdir -p $(DESTDIR)/usr/lib/lua/luci/template
|
mkdir -p $(DESTDIR)/usr/lib/ucode/luci
|
||||||
cp parser.so $(DESTDIR)/usr/lib/lua/luci/template/parser.so
|
cp core.so $(DESTDIR)/usr/lib/ucode/luci/core.so
|
||||||
cp version.lua $(DESTDIR)/usr/lib/lua/luci/version.lua
|
|
||||||
|
mkdir -p $(DESTDIR)/usr/share/ucode/luci
|
||||||
|
cp version.uc $(DESTDIR)/usr/share/ucode/luci/version.uc
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "template_lmo.h"
|
#include "lib/lmo.h"
|
||||||
|
|
||||||
static void die(const char *msg)
|
static void die(const char *msg)
|
||||||
{
|
{
|
||||||
|
@ -169,8 +169,11 @@ static void print_msg(struct msg *msg, FILE *out)
|
||||||
else
|
else
|
||||||
snprintf(key, sizeof(key), "%s", msg->id);
|
snprintf(key, sizeof(key), "%s", msg->id);
|
||||||
|
|
||||||
key_id = sfh_hash(key, strlen(key));
|
len = strlen(key);
|
||||||
val_id = sfh_hash(msg->val[i], strlen(msg->val[i]));
|
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) {
|
if (key_id != val_id) {
|
||||||
n_entries++;
|
n_entries++;
|
||||||
|
|
|
@ -47,7 +47,6 @@ const Class = {
|
||||||
if (!this.L) {
|
if (!this.L) {
|
||||||
this.L = this.env.dispatcher.load_luabridge().create();
|
this.L = this.env.dispatcher.load_luabridge().create();
|
||||||
this.L.set('L', proto({ write: print }, this.env));
|
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.L.invoke('require', 'luci.ucodebridge');
|
||||||
|
|
||||||
this.env.lua_active = true;
|
this.env.lua_active = true;
|
|
@ -12,7 +12,7 @@ LUCI_TYPE:=mod
|
||||||
LUCI_BASENAME:=compat
|
LUCI_BASENAME:=compat
|
||||||
|
|
||||||
LUCI_TITLE:=LuCI compatibility libraries
|
LUCI_TITLE:=LuCI compatibility libraries
|
||||||
LUCI_DEPENDS:=+luci-base
|
LUCI_DEPENDS:=+luci-lua-runtime
|
||||||
|
|
||||||
include ../../luci.mk
|
include ../../luci.mk
|
||||||
|
|
||||||
|
|
27
modules/luci-lua-runtime/Makefile
Normal file
27
modules/luci-lua-runtime/Makefile
Normal file
|
@ -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
|
26
modules/luci-lua-runtime/src/Makefile
Normal file
26
modules/luci-lua-runtime/src/Makefile
Normal file
|
@ -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
|
Loading…
Reference in a new issue