Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 0x4A6F-master
  • 0x4A6F-rpi4
  • autinerd/experimental-openwrt-24.10
  • experimental
  • feature/addMikrotikwAP
  • master
  • nrb/airmax-test
  • nrb/ar9344-reset-sequence
  • nrb/ex400-remove-wps
  • nrb/gluon-master-cpe510
  • nrb/test-radv-filter
  • nrbffs/fastd-remove-delay
  • nrbffs/netgear-ex6120
  • v2018.2.2-ffs
  • v2018.2.3-ffs
  • v2019.1-ffs
  • v2019.1.1-ffs
  • v2019.1.2-ffs
  • v2020.1-ffs
  • v2020.1.1-ffs
  • v2020.1.3-ffs
  • v2020.2-ffs
  • v2020.2.1-ffs
  • v2020.2.2-ffs
  • v2020.2.3-ffs
  • v2021.1-ffs
  • v2021.1.1-ffs
  • v2021.1.2-ffs
  • v2022.1.1-ffs
  • v2022.1.3-ffs
  • v2022.1.4-ffs
  • v2023.1-ffs
  • v2023.2-ffs
  • v2023.2.2-ffs
  • v2023.2.3-ffs
  • v2023.2.4-ffs
  • v2023.2.5-ffs
  • experimental-2022-09-24
  • experimental-2022-09-24-base
  • experimental-2023-03-11
  • experimental-2023-03-11-base
  • experimental-2023-03-12
  • experimental-2023-03-12-base
  • experimental-2023-03-16
  • experimental-2023-03-16-base
  • experimental-2023-03-20
  • experimental-2023-03-20-base
  • experimental-2023-03-23
  • experimental-2023-03-23-base
  • experimental-2023-03-25
  • experimental-2023-03-25-base
  • experimental-2023-03-26
  • experimental-2023-03-26-base
  • experimental-2023-03-30
  • experimental-2023-03-30-base
  • experimental-2023-03-31
  • experimental-2023-03-31-base
  • experimental-2023-04-01
  • experimental-2023-04-01-base
  • experimental-2023-04-08
  • experimental-2023-04-08-base
  • experimental-2023-04-10
  • experimental-2023-04-10-base
  • experimental-2023-04-13
  • experimental-2023-04-13-base
  • experimental-2023-04-15
  • experimental-2023-04-15-base
  • experimental-2023-04-16
  • experimental-2023-04-16-base
  • experimental-2023-04-18
  • experimental-2023-04-18-base
  • experimental-2023-04-20
  • experimental-2023-04-20-base
  • experimental-2023-04-26
  • experimental-2023-04-26-base
  • experimental-2023-04-28
  • experimental-2023-04-28-base
  • experimental-2023-04-30
  • experimental-2023-04-30-base
  • experimental-2023-05-02
  • experimental-2023-05-02-base
  • experimental-2023-05-03
  • experimental-2023-05-03-base
  • experimental-2023-05-12
  • experimental-2023-05-12-base
  • experimental-2023-05-21
  • experimental-2023-05-21-base
  • experimental-2023-05-25
  • experimental-2023-05-25-base
  • experimental-2023-07-02
  • experimental-2023-07-02-base
  • experimental-2023-07-04
  • experimental-2023-07-04-base
  • experimental-2023-07-12
  • experimental-2023-07-12-base
  • experimental-2023-07-16
  • experimental-2023-07-16-base
  • experimental-2023-08-04
  • experimental-2023-08-04-base
  • experimental-2023-08-10
  • experimental-2023-08-10-base
  • experimental-2023-09-08
  • experimental-2023-09-08-base
  • experimental-2023-09-09
  • experimental-2023-09-09-base
  • experimental-2023-09-10
  • experimental-2023-09-10-base
  • experimental-2023-09-11
  • experimental-2023-09-11-base
  • experimental-2023-09-12
  • experimental-2023-09-12-base
  • experimental-2023-09-13
  • experimental-2023-09-13-base
  • experimental-2023-09-15
  • experimental-2023-09-15-base
  • experimental-2023-09-16
  • experimental-2023-09-16-base
  • experimental-2023-09-18
  • experimental-2023-09-18-base
  • experimental-2023-09-20
  • experimental-2023-09-20-base
  • experimental-2023-09-27
  • experimental-2023-09-27-base
  • experimental-2023-09-28
  • experimental-2023-09-28-base
  • experimental-2023-09-29
  • experimental-2023-09-29-base
  • experimental-2023-10-02
  • experimental-2023-10-02-base
  • experimental-2023-10-13
  • experimental-2023-10-13-base
  • experimental-2023-10-14
  • experimental-2023-10-14-base
  • experimental-2023-10-16
  • experimental-2023-10-16-base
  • experimental-2023-10-23
  • experimental-2023-10-23-base
137 results

Target

Select target project
  • firmware/gluon
  • 0x4A6F/gluon
  • patrick/gluon
3 results
Select Git revision
  • 0x4A6F-master
  • 0x4A6F-rpi4
  • 2014.3.x
  • 2014.4.x
  • babel
  • hoodselector
  • master
  • radv-filterd
  • v2015.1.x
  • v2016.1.x
  • v2016.2.4-batmanbug
  • v2016.2.x
  • v2018.2.2-ffs
  • v2018.2.x
  • v2014.1
  • v2014.2
  • v2014.3
  • v2014.3.1
  • v2014.4
  • v2015.1
  • v2015.1.1
  • v2015.1.2
  • v2016.1
  • v2016.1.1
  • v2016.1.2
  • v2016.1.3
  • v2016.1.4
  • v2016.1.5
  • v2016.1.6
  • v2016.2
  • v2016.2.1
  • v2016.2.2
  • v2016.2.3
  • v2016.2.4
  • v2016.2.5
  • v2016.2.6
  • v2016.2.7
  • v2017.1
  • v2017.1.1
  • v2017.1.2
  • v2017.1.3
  • v2017.1.4
  • v2017.1.5
  • v2017.1.6
  • v2017.1.7
  • v2017.1.8
  • v2018.1
  • v2018.1.1
  • v2018.1.2
  • v2018.1.3
  • v2018.1.4
  • v2018.2
  • v2018.2-ffs0.1
  • v2018.2.1
  • v2018.2.1-ffs0.1
  • v2018.2.2-ffs0.1
56 results
Show changes
Showing
with 1186 additions and 1079 deletions
......@@ -2,10 +2,9 @@
-- Copyright 2017 Matthias Schiffer <mschiffer@universe-factory.net>
-- Licensed to the public under the Apache License 2.0.
module("gluon.web.cgi", package.seeall)
local nixio = require("nixio")
require("gluon.web.http")
require("gluon.web.dispatcher")
local stdlib = require 'posix.stdlib'
local http = require 'gluon.web.http'
local dispatcher = require 'gluon.web.dispatcher'
-- Limited source to avoid endless blocking
local function limitsource(handle, limit)
......@@ -27,12 +26,11 @@ local function limitsource(handle, limit)
end
end
function run()
local http = gluon.web.http.Http(
nixio.getenv(),
limitsource(io.stdin, tonumber(nixio.getenv("CONTENT_LENGTH"))),
return function(config)
local env = stdlib.getenv()
dispatcher(config, http.Http(
env,
limitsource(io.stdin, tonumber(env.CONTENT_LENGTH)),
io.stdout
)
gluon.web.dispatcher.httpdispatch(http)
))
end
-- Copyright 2008 Steven Barth <steven@midlink.org>
-- Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org>
-- Copyright 2017 Matthias Schiffer <mschiffer@universe-factory.net>
-- Copyright 2017-2018 Matthias Schiffer <mschiffer@universe-factory.net>
-- Licensed to the public under the Apache License 2.0.
local fs = require "nixio.fs"
local glob = require 'posix.glob'
local json = require "jsonc"
local tpl = require "gluon.web.template"
local util = require "gluon.web.util"
local proto = require "gluon.web.http.protocol"
module("gluon.web.dispatcher", package.seeall)
function build_url(http, path)
local function build_url(http, path)
return (http:getenv("SCRIPT_NAME") or "") .. "/" .. table.concat(path, "/")
end
function redirect(http, ...)
http:redirect(build_url(http, {...}))
end
function node_visible(node)
return (
node.title and
node.target and
(not node.hidden)
)
end
function node_children(node)
if not node then return {} end
local ret = {}
for k, v in pairs(node.nodes) do
if node_visible(v) then
table.insert(ret, k)
end
end
table.sort(ret,
function(a, b)
return (node.nodes[a].order or 100)
< (node.nodes[b].order or 100)
end
)
return ret
end
function httpdispatch(http)
local request = {}
local pathinfo = proto.urldecode(http:getenv("PATH_INFO") or "", true)
for node in pathinfo:gmatch("[^/]+") do
table.insert(request, node)
end
ok, err = pcall(dispatch, http, request)
if not ok then
http:status(500, "Internal Server Error")
http:prepare_content("text/plain")
http:write(err)
end
end
local function set_language(renderer, accept)
local langs = {}
......@@ -77,7 +28,7 @@ local function set_language(renderer, accept)
end
for match in accept:gmatch("[^,]+") do
local lang = match:match('^%s*([^%s;-_]+)')
local lang = match:match('^%s*([^%s;_-]+)')
local q = tonumber(match:match(';q=(%S+)%s*$') or 1)
if lang == '*' then
......@@ -93,15 +44,10 @@ local function set_language(renderer, accept)
return (weights[a] or 0) > (weights[b] or 0)
end)
for _, lang in ipairs(langs) do
if renderer.setlanguage(lang) then
return
end
end
renderer.set_language(langs)
end
function dispatch(http, request)
local function dispatch(config, http, request)
local tree = {nodes={}}
local nodes = {[''] = tree}
......@@ -133,31 +79,43 @@ function dispatch(http, request)
return string.format(' %s="%s"', key, util.pcdata(tostring(val)))
end
local renderer = tpl.renderer(setmetatable({
local renderer = tpl(config, setmetatable({
http = http,
request = request,
node = function(path) return _node({path}) end,
write = function(...) return http:write(...) end,
pcdata = util.pcdata,
urlencode = proto.urlencode,
media = '/static/gluon',
theme = 'gluon',
resource = '/static/resources',
attr = attr,
json = json.stringify,
url = function(path) return build_url(http, path) end,
}, { __index = _G }))
local function createtree()
local base = config.base_path .. "/controller/"
local function load_ctl(path)
local ctl = assert(loadfile(path))
local _pkg
local subdisp = setmetatable({
package = function(name)
_pkg = name
end,
node = function(...)
return _node({...})
end,
entry = function(path, target, title, order)
local c = _node(path, true)
entry = function(entry_path, target, title, order)
local c = _node(entry_path, true)
c.target = target
c.title = title
c.order = order
c.pkg = _pkg
return c
end,
......@@ -176,32 +134,17 @@ function dispatch(http, request)
end
end,
template = function(view)
template = function(view, scope)
local pkg = _pkg
return function()
renderer.render("layout", {content = view})
renderer.render_layout(view, scope, pkg)
end
end,
model = function(name)
local pkg = _pkg
return function()
local hidenav = false
local model = require "gluon.web.model"
local maps = model.load(name, renderer)
for _, map in ipairs(maps) do
map:parse(http)
end
for _, map in ipairs(maps) do
map:handle()
hidenav = hidenav or map.hidenav
end
renderer.render("layout", {
content = "model/wrapper",
maps = maps,
hidenav = hidenav,
})
require('gluon.web.model')(config, http, renderer, name, pkg)
end
end,
......@@ -210,22 +153,16 @@ function dispatch(http, request)
end,
}, { __index = _G })
local function createtree()
local base = util.libpath() .. "/controller/"
local function load_ctl(path)
local ctl = assert(loadfile(path))
local env = setmetatable({}, { __index = subdisp })
setfenv(ctl, env)
ctl()
end
for path in (fs.glob(base .. "*.lua") or function() end) do
for _, path in ipairs(glob.glob(base .. "*.lua", 0) or {}) do
load_ctl(path)
end
for path in (fs.glob(base .. "*/*.lua") or function() end) do
for _, path in ipairs(glob.glob(base .. "*/*.lua", 0) or {}) do
load_ctl(path)
end
end
......@@ -239,21 +176,44 @@ function dispatch(http, request)
if not node or not node.target then
http:status(404, "Not Found")
renderer.render("layout", { content = "error404", message =
renderer.render_layout("error/404", {
message =
"No page is registered at '/" .. table.concat(request, "/") .. "'.\n" ..
"If this URL belongs to an extension, make sure it is properly installed.\n"
})
"If this URL belongs to an extension, make sure it is properly installed.\n",
}, 'gluon-web')
return
end
http:parse_input(node.filehandler)
local ok, err = pcall(http.parse_input, http, node.filehandler)
if not ok then
http:status(400, "Bad request")
http:prepare_content("text/plain")
http:write(err .. "\r\n")
return
end
local ok, err = pcall(node.target)
ok, err = pcall(node.target)
if not ok then
http:status(500, "Internal Server Error")
renderer.render("layout", { content = "error500", message =
renderer.render_layout("error/500", {
message =
"Failed to execute dispatcher target for entry '/" .. table.concat(request, "/") .. "'.\n" ..
"The called action terminated with an exception:\n" .. tostring(err or "(unknown)")
})
"The called action terminated with an exception:\n" .. tostring(err or "(unknown)"),
}, 'gluon-web')
end
end
return function(config, http)
local request = {}
local pathinfo = proto.urldecode(http:getenv("PATH_INFO") or "", true)
for node in pathinfo:gmatch("[^/]+") do
table.insert(request, node)
end
local ok, err = pcall(dispatch, config, http, request)
if not ok then
http:status(500, "Internal Server Error")
http:prepare_content("text/plain")
http:write(err .. "\r\n")
end
end
......@@ -2,18 +2,15 @@
-- Copyright 2017 Matthias Schiffer <mschiffer@universe-factory.net>
-- Licensed to the public under the Apache License 2.0.
local string = string
local table = table
local nixio = require "nixio"
local protocol = require "gluon.web.http.protocol"
local util = require "gluon.web.util"
local ipairs, pairs, tostring = ipairs, pairs, tostring
module "gluon.web.http"
local M = {}
local Http = util.class()
M.Http = Http
Http = util.class()
function Http:__init__(env, input, output)
self.input = input
self.output = output
......@@ -56,8 +53,8 @@ end
function Http:getcookie(name)
local c = string.gsub(";" .. (self:getenv("HTTP_COOKIE") or "") .. ";", "%s*;%s*", ";")
local p = ";" .. name .. "=(.-);"
local i, j, value = c:find(p)
return value and urldecode(value)
local _, _, value = c:find(p)
return value and protocol.urldecode(value)
end
function Http:getenv(name)
......@@ -81,13 +78,6 @@ end
function Http:prepare_content(mime)
if self.headers["content-type"] then return end
if mime == "application/xhtml+xml" then
local accept = self:getenv("HTTP_ACCEPT")
if not accept or not accept:find("application/xhtml+xml", nil, true) then
mime = "text/html; charset=UTF-8"
end
self:header("Vary", "Accept")
end
self:header("Content-Type", mime)
end
......@@ -121,3 +111,5 @@ function Http:redirect(url)
self:header("Location", url)
self:close()
end
return M
......@@ -3,12 +3,9 @@
-- Licensed to the public under the Apache License 2.0.
-- This class contains several functions useful for http message- and content
-- decoding and to retrive form data from raw http messages.
module("gluon.web.http.protocol", package.seeall)
HTTP_MAX_CONTENT = 1024*8 -- 8 kB maximum content size
-- decoding and to retrieve form data from raw http messages.
local M = {}
local function pump(src, snk)
while true do
......@@ -26,7 +23,7 @@ local function pump(src, snk)
end
end
function urlencode(s)
function M.urlencode(s)
return (string.gsub(s, '[^a-zA-Z0-9%-_%.~]',
function(c)
local ret = ''
......@@ -41,7 +38,7 @@ function urlencode(s)
end
-- the "+" sign to " " - and return the decoded string.
function urldecode(str, no_plus)
function M.urldecode(str, no_plus)
local function chrdec(hex)
return string.char(tonumber(hex, 16))
......@@ -75,7 +72,7 @@ end
-- 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)
function M.urldecode_params(url)
local params = {}
if url:find("?") then
......@@ -85,8 +82,8 @@ function urldecode_params(url)
for pair in url:gmatch("[^&;]+") do
-- find key and value
local key = urldecode(pair:match("^([^=]+)"))
local val = urldecode(pair:match("^[^=]+=(.+)$"))
local key = M.urldecode(pair:match("^([^=]+)"))
local val = M.urldecode(pair:match("^[^=]+=(.+)$"))
-- store
if key and key:len() > 0 then
......@@ -101,39 +98,34 @@ function urldecode_params(url)
end
-- Content-Type. Stores all extracted data associated with its parameter name
-- in the params table withing the given message object. Multiple parameter
-- in the params table within the given message object. Multiple parameter
-- values are stored as tables, ordinary ones as strings.
-- If an optional file callback function is given then it is feeded with the
-- If an optional file callback function is given then it is fed with the
-- file contents chunk by chunk and only the extracted file name is stored
-- within the params table. The callback function will be called subsequently
-- with three arguments:
-- o Table containing decoded (name, file) and raw (headers) mime header data
-- o String value containing a chunk of the file data
-- o Boolean which indicates wheather the current chunk is the last one (eof)
function mimedecode_message_body(src, msg, filecb)
if msg and msg.env.CONTENT_TYPE then
msg.mime_boundary = msg.env.CONTENT_TYPE:match("^multipart/form%-data; boundary=(.+)$")
-- o Boolean which indicates whether the current chunk is the last one (eof)
local function mimedecode_message_body(src, msg, filecb)
local mime_boundary = (msg.env.CONTENT_TYPE or ''):match("^multipart/form%-data; boundary=(.+)$")
if not mime_boundary then
error("Invalid Content-Type found")
end
if not msg.mime_boundary then
return nil, "Invalid Content-Type found"
end
local tlen = 0
local inhdr = false
local field = nil
local store = nil
local lchunk = nil
local function parse_headers(chunk, field)
local function parse_headers(chunk, pfield)
local stat
repeat
chunk, stat = chunk:gsub(
"^([A-Z][A-Za-z0-9%-_]+): +([^\r\n]+)\r\n",
function(k,v)
field.headers[k] = v
pfield.headers[k] = v
return ""
end
)
......@@ -143,26 +135,26 @@ function mimedecode_message_body(src, msg, filecb)
-- End of headers
if stat > 0 then
if field.headers["Content-Disposition"] then
if field.headers["Content-Disposition"]:match("^form%-data; ") then
field.name = field.headers["Content-Disposition"]:match('name="(.-)"')
field.file = field.headers["Content-Disposition"]:match('filename="(.+)"$')
if pfield.headers["Content-Disposition"] then
if pfield.headers["Content-Disposition"]:match("^form%-data; ") then
pfield.name = pfield.headers["Content-Disposition"]:match('name="(.-)"')
pfield.file = pfield.headers["Content-Disposition"]:match('filename="(.+)"$')
end
end
if not field.headers["Content-Type"] then
field.headers["Content-Type"] = "text/plain"
if not pfield.headers["Content-Type"] then
pfield.headers["Content-Type"] = "text/plain"
end
if field.name then
initval(msg.params, field.name)
if field.file then
appendval(msg.params, field.name, field.file)
if pfield.name then
initval(msg.params, pfield.name)
if pfield.file then
appendval(msg.params, pfield.name, pfield.file)
store = filecb
else
store = function(hdr, buf, eof)
appendval(msg.params, field.name, buf)
store = function(_, buf, _)
appendval(msg.params, pfield.name, buf)
end
end
else
......@@ -191,15 +183,16 @@ function mimedecode_message_body(src, msg, filecb)
local spos, epos, found
repeat
spos, epos = data:find("\r\n--" .. msg.mime_boundary .. "\r\n", 1, true)
spos, epos = data:find("\r\n--" .. mime_boundary .. "\r\n", 1, true)
if not spos then
spos, epos = data:find("\r\n--" .. msg.mime_boundary .. "--\r\n", 1, true)
spos, epos = data:find("\r\n--" .. mime_boundary .. "--\r\n", 1, true)
end
if spos then
local predata = data:sub(1, spos - 1)
local eof
if inhdr then
predata, eof = parse_headers(predata, field)
......@@ -228,11 +221,12 @@ function mimedecode_message_body(src, msg, filecb)
-- We found at least some boundary. Save
-- the unparsed remaining data for the
-- next chunk.
lchunk, data = data, nil
lchunk = data
else
-- There was a complete chunk without a boundary. Parse it as headers or
-- append it as data, depending on our current state.
if inhdr then
local eof
lchunk, eof = parse_headers(data, field)
inhdr = not eof
else
......@@ -243,7 +237,7 @@ function mimedecode_message_body(src, msg, filecb)
if store then
store(field, lchunk, false)
end
lchunk, chunk = chunk, nil
lchunk = chunk
end
end
end
......@@ -251,18 +245,61 @@ function mimedecode_message_body(src, msg, filecb)
return true
end
return pump(src, snk)
assert(pump(src, snk))
end
local function check_post_origin(msg)
local default_port = '80'
local request_scheme = 'http'
if msg.env.HTTPS then
default_port = '443'
request_scheme = 'https'
end
local request_host = msg.env.HTTP_HOST
if not request_host then
error('POST request without Host header')
end
if not request_host:match(':[0-9]+$') then
request_host = request_host .. ':' .. default_port
end
local origin = msg.env.HTTP_ORIGIN
if not origin then
error('POST request without Origin header')
end
local origin_scheme, origin_host = origin:match('^([^:]*)://(.*)$')
if not origin_host then
error('POST request with invalid Origin header')
end
if not origin_host:match(':[0-9]+$') then
local origin_port
if origin_scheme == 'http' then
origin_port = '80'
elseif origin_scheme == 'https' then
origin_port = '443'
else
error('POST request with invalid Origin header')
end
origin_host = origin_host .. ':' .. origin_port
end
if request_scheme ~= origin_scheme or request_host ~= origin_host then
error('Invalid cross-origin POST')
end
end
-- This function will examine the Content-Type within the given message object
-- to select the appropriate content decoder.
-- Currently only the multipart/form-data mime type is supported.
function parse_message_body(src, msg, filecb)
if not (msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE) then
function M.parse_message_body(src, msg, filecb)
if msg.env.REQUEST_METHOD ~= "POST" then
return
end
if msg.env.CONTENT_TYPE:match("^multipart/form%-data") then
return mimedecode_message_body(src, msg, filecb)
end
check_post_origin(msg)
mimedecode_message_body(src, msg, filecb)
end
return M
-- Copyright 2018 Matthias Schiffer <mschiffer@universe-factory.net>
-- Licensed to the public under the Apache License 2.0.
local tparser = require 'gluon.web.template.parser'
local unistd = require 'posix.unistd'
return function(config)
local i18ndir = config.base_path .. "/i18n"
local function i18n_file(lang, pkg)
return string.format('%s/%s.%s.lmo', i18ndir, pkg, lang)
end
local function no_translation()
return nil
end
local function load_catalog(lang, pkg)
if pkg then
local file = i18n_file(lang, pkg)
local cat = unistd.access(file) and tparser.load_catalog(file)
if cat then return cat end
end
return no_translation
end
local i18n = {}
function i18n.supported(lang)
return lang == 'en' or unistd.access(i18n_file(lang, 'gluon-web'))
end
function i18n.load(lang, pkg)
local _translate = load_catalog(lang, pkg)
local function translate(key)
return _translate(key) or key
end
local function translatef(key, ...)
return translate(key):format(...)
end
return {
_translate = _translate,
translate = translate,
translatef = translatef,
}
end
return i18n
end
-- Copyright 2008 Steven Barth <steven@midlink.org>
-- Copyright 2017 Matthias Schiffer <mschiffer@universe-factory.net>
-- Copyright 2017-2018 Matthias Schiffer <mschiffer@universe-factory.net>
-- Licensed to the public under the Apache License 2.0.
local tparser = require "gluon.web.template.parser"
local util = require "gluon.web.util"
local fs = require "nixio.fs"
local tparser = require 'gluon.web.template.parser'
local tostring, setmetatable, setfenv, pcall, assert = tostring, setmetatable, setfenv, pcall, assert
local tostring, ipairs, setmetatable, setfenv = tostring, ipairs, setmetatable, setfenv
local pcall, assert = pcall, assert
module "gluon.web.template"
return function(config, env)
local i18n = require('gluon.web.i18n')(config)
local viewdir = util.libpath() .. "/view/"
local i18ndir = util.libpath() .. "/i18n/"
local viewdir = config.base_path .. '/view/'
function renderer(env)
local ctx = {}
local language = 'en'
local catalogs = {}
local function render_template(name, template, scope)
function ctx.set_language(langs)
for _, lang in ipairs(langs) do
if i18n.supported(lang) then
language = lang
catalogs = {}
return
end
end
end
function ctx.i18n(pkg)
local cat = catalogs[pkg] or i18n.load(language, pkg)
if pkg then catalogs[pkg] = cat end
return cat
end
local function render_template(name, template, scope, pkg)
scope = scope or {}
local t = ctx.i18n(pkg)
local locals = {
renderer = ctx,
translate = ctx.translate,
translatef = ctx.translatef,
_translate = ctx._translate,
include = function(name)
ctx.render(name, scope)
i18n = ctx.i18n,
translate = t.translate,
translatef = t.translatef,
_translate = t._translate,
include = function(include_name)
ctx.render(include_name, scope, pkg)
end,
}
setfenv(template, setmetatable({}, {
__index = function(tbl, key)
return scope[key] or env[key] or locals[key]
__index = function(_, key)
return scope[key] or locals[key] or env[key]
end
}))
-- Now finally render the thing
local stat, err = pcall(template)
assert(stat, "Failed to execute template '" .. name .. "'.\n" ..
"A runtime error occured: " .. tostring(err or "(nil)"))
"A runtime error occurred: " .. tostring(err or "(nil)"))
end
--- Render a certain template.
-- @param name Template name
-- @param scope Scope to assign to template (optional)
function ctx.render(name, scope)
-- @param pkg i18n namespace package (optional)
function ctx.render(name, scope, pkg)
local sourcefile = viewdir .. name .. ".html"
local template, _, err = tparser.parse(sourcefile)
......@@ -54,45 +73,35 @@ function renderer(env)
"Error while parsing template '" .. sourcefile .. "':\n" ..
(err or "Unknown syntax error"))
render_template(name, template, scope)
render_template(name, template, scope, pkg)
end
--- Render a template from a string.
-- @param template Template string
-- @param scope Scope to assign to template (optional)
function ctx.render_string(str, scope)
-- @param pkg i18n namespace package (optional)
function ctx.render_string(str, scope, pkg)
local template, _, err = tparser.parse_string(str)
assert(template, "Error while parsing template:\n" ..
(err or "Unknown syntax error"))
render_template('(local)', template, scope)
end
function ctx.setlanguage(lang)
lang = lang:gsub("_", "-")
if not lang then return false end
if lang ~= 'en' and not fs.access(i18ndir .. "gluon-web." .. lang .. ".lmo") then
return false
render_template('(local)', template, scope, pkg)
end
return tparser.load_catalog(lang, i18ndir)
end
-- Returns a translated string, or nil if none is found
function ctx._translate(key)
return (tparser.translate(key))
end
-- Returns a translated string, or the original string if none is found
function ctx.translate(key)
return tparser.translate(key) or key
end
function ctx.translatef(key, ...)
local t = ctx.translate(key)
return t:format(...)
--- Render a template, wrapped in the configured layout.
-- @param name Template name
-- @param scope Scope to assign to template (optional)
-- @param pkg i18n namespace package (optional)
-- @param layout_scope Additional variables to pass to the layout template
function ctx.render_layout(name, scope, pkg, layout_scope)
ctx.render(config.layout_template, setmetatable({
content = name,
scope = scope,
pkg = pkg,
}, {
__index = layout_scope
}), config.layout_package)
end
return ctx
......
......@@ -2,16 +2,10 @@
-- Copyright 2017 Matthias Schiffer <mschiffer@universe-factory.net>
-- Licensed to the public under the Apache License 2.0.
local io = require "io"
local table = require "table"
local tparser = require "gluon.web.template.parser"
local nixio = require "nixio"
local fs = require "nixio.fs"
local getmetatable, setmetatable = getmetatable, setmetatable
local tostring, pairs = tostring, pairs
module "gluon.web.util"
local M = {}
--
-- Class helper routines
......@@ -37,14 +31,14 @@ end
-- 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)
function M.class(base)
return setmetatable({}, {
__call = _instantiate,
__index = base
})
end
function instanceof(object, class)
function M.instanceof(object, class)
while object do
if object == class then
return true
......@@ -60,38 +54,8 @@ end
-- String and data manipulation routines
--
function pcdata(value)
function M.pcdata(value)
return value and tparser.pcdata(tostring(value))
end
function contains(table, value)
for k, v in pairs(table) do
if value == v then
return k
end
end
return false
end
--
-- System utility functions
--
function exec(command)
local pp = io.popen(command)
local data = pp:read("*a")
pp:close()
return data
end
function uniqueid(bytes)
local rand = fs.readfile("/dev/urandom", bytes)
return nixio.bin.hexlify(rand)
end
function libpath()
return '/lib/gluon/web'
end
return M
all: compile
%.o: %.c
$(CC) $(CPPFLAGS) $(CFLAGS) -fPIC -c -o $@ $<
$(CC) $(CPPFLAGS) $(CFLAGS) -D_GNU_SOURCE -std=c99 -Wall -Wextra -fPIC -fvisibility=hidden -c -o $@ $<
clean:
rm -f parser.so *.o
......@@ -9,6 +9,8 @@ clean:
parser.so: template_parser.o template_utils.o template_lmo.o template_lualib.o
$(CC) $(LDFLAGS) -shared -o $@ $^
gluon-po2lmo: gluon-po2lmo.o template_lmo.o
compile: parser.so
install: compile
......
/*
* lmo - Lua Machine Objects - PO to LMO conversion tool
*
* Copyright (C) 2009-2012 Jo-Philipp Wich <jow@openwrt.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "template_lmo.h"
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
__attribute__((noreturn))
static void die(const char *msg)
{
fprintf(stderr, "Error: %s\n", msg);
exit(1);
}
__attribute__((noreturn))
static void usage(const char *name)
{
fprintf(stderr, "Usage: %s input.po output.lmo\n", name);
exit(1);
}
static void print(const void *ptr, size_t size, size_t nmemb, FILE *stream)
{
if( fwrite(ptr, size, nmemb, stream) == 0 )
die("Failed to write stdout");
}
static ssize_t extract_string(const char *src, char *dest, size_t len)
{
size_t pos = 0;
int esc = 0;
int off = -1;
for( pos = 0; (pos < strlen(src)) && (pos < len); pos++ )
{
if( (off == -1) && (src[pos] == '"') )
{
off = pos + 1;
}
else if( off >= 0 )
{
if( esc == 1 )
{
switch (src[pos])
{
case '"':
case '\\':
off++;
break;
}
dest[pos-off] = src[pos];
esc = 0;
}
else if( src[pos] == '\\' )
{
dest[pos-off] = src[pos];
esc = 1;
}
else if( src[pos] != '"' )
{
dest[pos-off] = src[pos];
}
else
{
dest[pos-off] = '\0';
break;
}
}
}
return (off > -1) ? (ssize_t) strlen(dest) : -1;
}
static int cmp_index(const void *a, const void *b)
{
uint32_t x = ((const lmo_entry_t *)a)->key_id;
uint32_t y = ((const lmo_entry_t *)b)->key_id;
if (x < y)
return -1;
else if (x > y)
return 1;
return 0;
}
static void print_uint32(uint32_t x, FILE *out)
{
uint32_t y = htonl(x);
print(&y, sizeof(uint32_t), 1, out);
}
static void print_index(void *array, int n, FILE *out)
{
lmo_entry_t *e;
qsort(array, n, sizeof(*e), cmp_index);
for (e = array; n > 0; n--, e++)
{
print_uint32(e->key_id, out);
print_uint32(e->val_id, out);
print_uint32(e->offset, out);
print_uint32(e->length, out);
}
}
int main(int argc, char *argv[])
{
char line[4096];
char key[4096];
char val[4096];
char tmp[4096];
int state = 0;
int offset = 0;
int length = 0;
int n_entries = 0;
void *array = NULL;
lmo_entry_t *entry = NULL;
uint32_t key_id, val_id;
FILE *in;
FILE *out;
if( (argc != 3) || ((in = fopen(argv[1], "r")) == NULL) || ((out = fopen(argv[2], "w")) == NULL) )
usage(argv[0]);
memset(line, 0, sizeof(key));
memset(key, 0, sizeof(val));
memset(val, 0, sizeof(val));
while( (NULL != fgets(line, sizeof(line), in)) || (state >= 2 && feof(in)) )
{
if( state == 0 && strstr(line, "msgid \"") == line )
{
switch(extract_string(line, key, sizeof(key)))
{
case -1:
die("Syntax error in msgid");
case 0:
state = 1;
break;
default:
state = 2;
}
}
else if( state == 1 || state == 2 )
{
if( strstr(line, "msgstr \"") == line || state == 2 )
{
switch(extract_string(line, val, sizeof(val)))
{
case -1:
state = 4;
break;
default:
state = 3;
}
}
else
{
switch(extract_string(line, tmp, sizeof(tmp)))
{
case -1:
state = 2;
break;
default:
strcat(key, tmp);
}
}
}
else if( state == 3 )
{
switch(extract_string(line, tmp, sizeof(tmp)))
{
case -1:
state = 4;
break;
default:
strcat(val, tmp);
}
}
if( state == 4 )
{
if( strlen(key) > 0 && strlen(val) > 0 )
{
key_id = sfh_hash(key, strlen(key));
val_id = sfh_hash(val, strlen(val));
if( key_id != val_id )
{
n_entries++;
array = realloc(array, n_entries * sizeof(lmo_entry_t));
entry = (lmo_entry_t *)array + n_entries - 1;
if (!array)
die("Out of memory");
entry->key_id = key_id;
entry->val_id = val_id;
entry->offset = offset;
entry->length = strlen(val);
length = strlen(val) + ((4 - (strlen(val) % 4)) % 4);
print(val, length, 1, out);
offset += length;
}
}
state = 0;
memset(key, 0, sizeof(key));
memset(val, 0, sizeof(val));
}
memset(line, 0, sizeof(line));
}
print_index(array, n_entries, out);
if( offset > 0 )
{
print_uint32(offset, out);
fsync(fileno(out));
fclose(out);
}
else
{
fclose(out);
unlink(argv[2]);
}
fclose(in);
return(0);
}
......@@ -2,6 +2,7 @@
* lmo - Lua Machine Objects - Base functions
*
* Copyright (C) 2009-2010 Jo-Philipp Wich <jow@openwrt.org>
* Copyright (C) 2018 Matthias Schiffer <mschiffer@universe-factory.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -18,38 +19,56 @@
#include "template_lmo.h"
#include <sys/stat.h>
#include <sys/mman.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static inline uint16_t get_le16(const void *data) {
const uint8_t *d = data;
return (((uint16_t)d[1]) << 8) | d[0];
}
static inline uint32_t get_be32(const void *data) {
const uint8_t *d = data;
return (((uint32_t)d[0]) << 24)
| (((uint32_t)d[1]) << 16)
| (((uint32_t)d[2]) << 8)
| d[3];
}
/*
* Hash function from http://www.azillionmonkeys.com/qed/hash.html
* Copyright (C) 2004-2008 by Paul Hsieh
*/
static uint32_t sfh_hash(const char *data, int len)
uint32_t sfh_hash(const void *input, size_t len)
{
const uint8_t *data = input;
uint32_t hash = len, tmp;
int rem;
if (len <= 0 || data == NULL) return 0;
rem = len & 3;
len >>= 2;
/* Main loop */
for (;len > 0; len--) {
hash += sfh_get16(data);
tmp = (sfh_get16(data+2) << 11) ^ hash;
for (; len > 3; len -= 4) {
hash += get_le16(data);
tmp = (get_le16(data+2) << 11) ^ hash;
hash = (hash << 16) ^ tmp;
data += 2*sizeof(uint16_t);
data += 4;
hash += hash >> 11;
}
/* Handle end cases */
switch (rem) {
case 3: hash += sfh_get16(data);
switch (len) {
case 3: hash += get_le16(data);
hash ^= hash << 16;
hash ^= data[sizeof(uint16_t)] << 18;
hash ^= data[2] << 18;
hash += hash >> 11;
break;
case 2: hash += sfh_get16(data);
case 2: hash += get_le16(data);
hash ^= hash << 11;
hash += hash >> 17;
break;
......@@ -69,220 +88,90 @@ static uint32_t sfh_hash(const char *data, int len)
return hash;
}
static uint32_t lmo_canon_hash(const char *str, int len)
{
char res[4096];
char *ptr, prev;
int off;
if (!str || len >= sizeof(res))
return 0;
for (prev = ' ', ptr = res, off = 0; off < len; prev = *str, off++, str++)
{
if (isspace(*str))
{
if (!isspace(prev))
*ptr++ = ' ';
}
else
{
*ptr++ = *str;
}
}
if ((ptr > res) && isspace(*(ptr-1)))
ptr--;
return sfh_hash(res, ptr - res);
}
static lmo_archive_t * lmo_open(const char *file)
bool lmo_load(lmo_catalog_t *cat, const char *file)
{
int in = -1;
uint32_t idx_offset = 0;
int fd = -1;
struct stat s;
lmo_archive_t *ar = NULL;
cat->data = MAP_FAILED;
if (stat(file, &s) == -1)
fd = open(file, O_RDONLY|O_CLOEXEC);
if (fd < 0)
goto err;
if ((in = open(file, O_RDONLY)) == -1)
if (fstat(fd, &s))
goto err;
if ((ar = (lmo_archive_t *)malloc(sizeof(*ar))) != NULL)
{
memset(ar, 0, sizeof(*ar));
ar->fd = in;
ar->size = s.st_size;
cat->data = mmap(NULL, s.st_size, PROT_READ, MAP_SHARED, fd, 0);
fcntl(ar->fd, F_SETFD, fcntl(ar->fd, F_GETFD) | FD_CLOEXEC);
close(fd);
fd = -1;
if ((ar->mmap = mmap(NULL, ar->size, PROT_READ, MAP_SHARED, ar->fd, 0)) == MAP_FAILED)
if (cat->data == MAP_FAILED)
goto err;
idx_offset = ntohl(*((const uint32_t *)
(ar->mmap + ar->size - sizeof(uint32_t))));
cat->end = cat->data + s.st_size;
uint32_t idx_offset = get_be32(cat->end - sizeof(uint32_t));
cat->index = (const lmo_entry_t *)(cat->data + idx_offset);
if (idx_offset >= ar->size)
if ((const char *)cat->index > (cat->end - sizeof(uint32_t)))
goto err;
ar->index = (lmo_entry_t *)(ar->mmap + idx_offset);
ar->length = (ar->size - idx_offset - sizeof(uint32_t)) / sizeof(lmo_entry_t);
ar->end = ar->mmap + ar->size;
cat->length = (cat->end - sizeof(uint32_t) - (const char *)cat->index) / sizeof(lmo_entry_t);
return ar;
}
return true;
err:
if (in > -1)
close(in);
if (fd >= 0)
close(fd);
if (ar != NULL)
{
if ((ar->mmap != NULL) && (ar->mmap != MAP_FAILED))
munmap(ar->mmap, ar->size);
free(ar);
}
if (cat->data != MAP_FAILED)
munmap(cat->data, cat->end - cat->data);
return NULL;
return false;
}
static lmo_catalog_t *_lmo_catalogs;
static lmo_catalog_t *_lmo_active_catalog;
int lmo_load_catalog(const char *lang, const char *dir)
{
DIR *dh = NULL;
char pattern[16];
char path[PATH_MAX];
struct dirent *de = NULL;
lmo_archive_t *ar = NULL;
lmo_catalog_t *cat = NULL;
if (!lmo_change_catalog(lang))
return 0;
if (!dir || !(dh = opendir(dir)))
goto err;
if (!(cat = malloc(sizeof(*cat))))
goto err;
memset(cat, 0, sizeof(*cat));
snprintf(cat->lang, sizeof(cat->lang), "%s", lang);
snprintf(pattern, sizeof(pattern), "*.%s.lmo", lang);
while ((de = readdir(dh)) != NULL)
{
if (!fnmatch(pattern, de->d_name, 0))
{
snprintf(path, sizeof(path), "%s/%s", dir, de->d_name);
ar = lmo_open(path);
if (ar)
void lmo_unload(lmo_catalog_t *cat)
{
ar->next = cat->archives;
cat->archives = ar;
}
if (cat->data != MAP_FAILED)
munmap(cat->data, cat->end - cat->data);
}
}
closedir(dh);
cat->next = _lmo_catalogs;
_lmo_catalogs = cat;
if (!_lmo_active_catalog)
_lmo_active_catalog = cat;
return 0;
err:
if (dh) closedir(dh);
if (cat) free(cat);
return -1;
}
int lmo_change_catalog(const char *lang)
static int lmo_compare_entry(const void *a, const void *b)
{
lmo_catalog_t *cat;
for (cat = _lmo_catalogs; cat; cat = cat->next)
{
if (!strncmp(cat->lang, lang, sizeof(cat->lang)))
{
_lmo_active_catalog = cat;
return 0;
}
}
const lmo_entry_t *ea = a, *eb = b;
uint32_t ka = ntohl(ea->key_id), kb = ntohl(eb->key_id);
if (ka < kb)
return -1;
else if (ka > kb)
return 1;
else
return 0;
}
static lmo_entry_t * lmo_find_entry(lmo_archive_t *ar, uint32_t hash)
{
unsigned int m, l, r;
uint32_t k;
l = 0;
r = ar->length - 1;
while (1)
{
m = l + ((r - l) / 2);
if (r < l)
break;
k = ntohl(ar->index[m].key_id);
if (k == hash)
return &ar->index[m];
if (k > hash)
static const lmo_entry_t * lmo_find_entry(const lmo_catalog_t *cat, uint32_t hash)
{
if (!m)
break;
lmo_entry_t key;
key.key_id = htonl(hash);
r = m - 1;
}
else
{
l = m + 1;
}
return bsearch(&key, cat->index, cat->length, sizeof(lmo_entry_t), lmo_compare_entry);
}
return NULL;
}
int lmo_translate(const char *key, int keylen, char **out, int *outlen)
bool lmo_translate(const lmo_catalog_t *cat, const char *key, size_t keylen, const char **out, size_t *outlen)
{
uint32_t hash;
lmo_entry_t *e;
lmo_archive_t *ar;
if (!key || !_lmo_active_catalog)
return -2;
uint32_t hash = sfh_hash(key, keylen);
const lmo_entry_t *e = lmo_find_entry(cat, hash);
if (!e)
return false;
hash = lmo_canon_hash(key, keylen);
for (ar = _lmo_active_catalog->archives; ar; ar = ar->next)
{
if ((e = lmo_find_entry(ar, hash)) != NULL)
{
*out = ar->mmap + ntohl(e->offset);
*out = cat->data + ntohl(e->offset);
*outlen = ntohl(e->length);
return 0;
}
}
return -1;
if (*out + *outlen > cat->end)
return false;
return true;
}
......@@ -2,6 +2,7 @@
* lmo - Lua Machine Objects - General header
*
* Copyright (C) 2009-2012 Jo-Philipp Wich <jow@openwrt.org>
* Copyright (C) 2018 Matthias Schiffer <mschiffer@universe-factory.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -19,27 +20,9 @@
#ifndef _TEMPLATE_LMO_H_
#define _TEMPLATE_LMO_H_
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <fnmatch.h>
#include <dirent.h>
#include <ctype.h>
#include <limits.h>
#if (defined(__GNUC__) && defined(__i386__))
#define sfh_get16(d) (*((const uint16_t *) (d)))
#else
#define sfh_get16(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
+(uint32_t)(((const uint8_t *)(d))[0]) )
#endif
struct lmo_entry {
......@@ -48,34 +31,23 @@ struct lmo_entry {
uint32_t offset;
uint32_t length;
} __attribute__((packed));
typedef struct lmo_entry lmo_entry_t;
struct lmo_archive {
int fd;
int length;
uint32_t size;
lmo_entry_t *index;
char *mmap;
char *end;
struct lmo_archive *next;
};
typedef struct lmo_archive lmo_archive_t;
struct lmo_catalog {
char lang[6];
struct lmo_archive *archives;
struct lmo_catalog *next;
size_t length;
const lmo_entry_t *index;
char *data;
const char *end;
};
typedef struct lmo_catalog lmo_catalog_t;
int lmo_load_catalog(const char *lang, const char *dir);
int lmo_change_catalog(const char *lang);
int lmo_translate(const char *key, int keylen, char **out, int *outlen);
uint32_t sfh_hash(const void *input, size_t len);
bool lmo_load(lmo_catalog_t *cat, const char *file);
void lmo_unload(lmo_catalog_t *cat);
bool lmo_translate(const lmo_catalog_t *cat, const char *key, size_t keylen, const char **out, size_t *outlen);
#endif
/*
* LuCI Template - Lua binding
* gluon-web Template - Lua binding
*
* Copyright (C) 2009 Jo-Philipp Wich <jow@openwrt.org>
* Copyright (C) 2018 Matthias Schiffer <mschiffer@universe-factory.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -16,7 +17,20 @@
* limitations under the License.
*/
#include "template_lualib.h"
#include "template_parser.h"
#include "template_utils.h"
#include "template_lmo.h"
#include <lualib.h>
#include <lauxlib.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#define TEMPLATE_CATALOG "gluon.web.template.parser.catalog"
static int template_L_do_parse(lua_State *L, struct template_parser *parser, const char *chunkname)
{
......@@ -61,61 +75,76 @@ static int template_L_parse_string(lua_State *L)
static int template_L_pcdata(lua_State *L)
{
size_t len = 0;
const char *str = luaL_checklstring(L, 1, &len);
char *res = pcdata(str, len);
size_t inlen, outlen;
char *out;
const char *in = luaL_checklstring(L, 1, &inlen);
if (!pcdata(in, inlen, &out, &outlen))
return 0;
if (res != NULL)
{
lua_pushstring(L, res);
free(res);
lua_pushlstring(L, out, outlen);
free(out);
return 1;
}
static int template_L_load_catalog(lua_State *L)
{
const char *file = luaL_checkstring(L, 1);
lmo_catalog_t *cat = lua_newuserdata(L, sizeof(*cat));
if (!lmo_load(cat, file)) {
lua_pop(L, 1);
return 0;
}
static int template_L_load_catalog(lua_State *L) {
const char *lang = luaL_optstring(L, 1, "en");
const char *dir = luaL_optstring(L, 2, NULL);
lua_pushboolean(L, !lmo_load_catalog(lang, dir));
luaL_getmetatable(L, TEMPLATE_CATALOG);
lua_setmetatable(L, -2);
return 1;
}
static int template_L_translate(lua_State *L) {
size_t len;
char *tr;
int trlen;
const char *key = luaL_checklstring(L, 1, &len);
switch (lmo_translate(key, len, &tr, &trlen))
static int template_catalog_call(lua_State *L)
{
case 0:
lua_pushlstring(L, tr, trlen);
return 1;
case -1:
size_t inlen, outlen;
lmo_catalog_t *cat = luaL_checkudata(L, 1, TEMPLATE_CATALOG);
const char *in = luaL_checklstring(L, 2, &inlen), *out;
if (!lmo_translate(cat, in, inlen, &out, &outlen))
return 0;
}
lua_pushnil(L);
lua_pushstring(L, "no catalog loaded");
return 2;
lua_pushlstring(L, out, outlen);
return 1;
}
static int template_catalog_gc(lua_State *L)
{
lmo_catalog_t *cat = luaL_checkudata(L, 1, TEMPLATE_CATALOG);
lmo_unload(cat);
return 0;
}
/* module table */
static const luaL_reg R[] = {
{ "parse", template_L_parse },
{ "parse_string", template_L_parse_string },
{ "pcdata", template_L_pcdata },
{ "load_catalog", template_L_load_catalog },
{ "translate", template_L_translate },
{}
};
static const luaL_reg template_catalog_methods[] = {
{ "__call", template_catalog_call },
{ "__gc", template_catalog_gc },
{}
};
__attribute__ ((visibility("default")))
LUALIB_API int luaopen_gluon_web_template_parser(lua_State *L) {
luaL_register(L, TEMPLATE_LUALIB_META, R);
luaL_register(L, "gluon.web.template.parser", R);
luaL_newmetatable(L, TEMPLATE_CATALOG);
luaL_register(L, NULL, template_catalog_methods);
lua_pop(L, 1);
return 1;
}
/*
* LuCI Template - Lua library header
*
* Copyright (C) 2009 Jo-Philipp Wich <jow@openwrt.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _TEMPLATE_LUALIB_H_
#define _TEMPLATE_LUALIB_H_
#include "template_parser.h"
#include "template_utils.h"
#include "template_lmo.h"
#define TEMPLATE_LUALIB_META "gluon.web.template.parser"
LUALIB_API int luaopen_gluon_web_template_parser(lua_State *L);
#endif
/*
* LuCI Template - Parser implementation
* gluon-web Template - Parser implementation
*
* Copyright (C) 2009-2012 Jo-Philipp Wich <jow@openwrt.org>
* Copyright (C) 2018 Matthias Schiffer <mschiffer@universe-factory.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -20,110 +21,129 @@
#include "template_utils.h"
#include "template_lmo.h"
#include <lualib.h>
#include <lauxlib.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
typedef enum {
T_TYPE_INIT,
T_TYPE_TEXT,
T_TYPE_COMMENT,
T_TYPE_EXPR,
T_TYPE_EXPR_RAW,
T_TYPE_INCLUDE,
T_TYPE_I18N,
T_TYPE_I18N_RAW,
T_TYPE_CODE,
T_TYPE_EOF,
} t_type_t;
struct template_chunk {
const char *s;
const char *e;
t_type_t type;
int line;
};
/* leading and trailing code for different types */
static const char *const gen_code[9][2] = {
{NULL, NULL},
{"write(\"", "\")"},
{NULL, NULL},
{"write(tostring(", " or \"\"))"},
{"include(\"", "\")"},
{"write(\"", "\")"},
{"write(\"", "\")"},
{NULL, " "},
{}
/* parser state */
struct template_parser {
size_t size;
char *data;
char *off;
char *lua_chunk;
int line;
int in_expr;
bool strip_before;
bool strip_after;
struct template_chunk prv_chunk;
struct template_chunk cur_chunk;
const char *file;
};
/* Simple strstr() like function that takes len arguments for both haystack and needle. */
static char *strfind(char *haystack, int hslen, const char *needle, int ndlen)
{
int match = 0;
int i, j;
for( i = 0; i < hslen; i++ )
{
if( haystack[i] == needle[0] )
{
match = ((ndlen == 1) || ((i + ndlen) <= hslen));
/* leading and trailing code for different types */
static const char *const gen_code[][2] = {
[T_TYPE_INIT] = {NULL, NULL},
[T_TYPE_TEXT] = {"write('", "')"},
[T_TYPE_COMMENT] = {NULL, NULL},
[T_TYPE_EXPR] = {"write(pcdata(tostring(", " or '')))"},
[T_TYPE_EXPR_RAW] = {"write(tostring(", " or ''))"},
[T_TYPE_INCLUDE] = {"include('", "')"},
[T_TYPE_I18N] = {"write(pcdata(translate('", "')))"},
[T_TYPE_I18N_RAW] = {"write(translate('", "'))"},
[T_TYPE_CODE] = {NULL, " "},
[T_TYPE_EOF] = {NULL, NULL},
};
for( j = 1; (j < ndlen) && ((i + j) < hslen); j++ )
{
if( haystack[i+j] != needle[j] )
static struct template_parser * template_init(struct template_parser *parser)
{
match = 0;
break;
}
}
if( match )
return &haystack[i];
}
}
parser->off = parser->data;
parser->cur_chunk.type = T_TYPE_INIT;
parser->cur_chunk.s = parser->data;
parser->cur_chunk.e = parser->data;
return NULL;
return parser;
}
struct template_parser * template_open(const char *file)
{
int fd = -1;
struct stat s;
struct template_parser *parser;
if (!(parser = malloc(sizeof(*parser))))
if (!(parser = calloc(1, sizeof(*parser))))
goto err;
memset(parser, 0, sizeof(*parser));
parser->fd = -1;
parser->file = file;
if (stat(file, &s))
fd = open(file, O_RDONLY|O_CLOEXEC);
if (fd < 0)
goto err;
if ((parser->fd = open(file, O_RDONLY)) < 0)
if (fstat(fd, &s))
goto err;
parser->size = s.st_size;
parser->data = mmap(NULL, parser->size, PROT_READ, MAP_PRIVATE,
parser->fd, 0);
fd, 0);
if (parser->data != MAP_FAILED)
{
parser->off = parser->data;
parser->cur_chunk.type = T_TYPE_INIT;
parser->cur_chunk.s = parser->data;
parser->cur_chunk.e = parser->data;
close(fd);
fd = -1;
return parser;
}
if (parser->data == MAP_FAILED)
goto err;
return template_init(parser);
err:
if (fd >= 0)
close(fd);
template_close(parser);
return NULL;
}
struct template_parser * template_string(const char *str, uint32_t len)
struct template_parser * template_string(const char *str, size_t len)
{
struct template_parser *parser;
if (!str) {
errno = EINVAL;
return NULL;
}
if (!(parser = malloc(sizeof(*parser))))
if (!(parser = calloc(1, sizeof(*parser))))
goto err;
memset(parser, 0, sizeof(*parser));
parser->fd = -1;
parser->size = len;
parser->data = (char *)str;
parser->off = parser->data;
parser->cur_chunk.type = T_TYPE_INIT;
parser->cur_chunk.s = parser->data;
parser->cur_chunk.e = parser->data;
return parser;
return template_init(parser);
err:
template_close(parser);
......@@ -135,16 +155,12 @@ void template_close(struct template_parser *parser)
if (!parser)
return;
if (parser->gc != NULL)
free(parser->gc);
free(parser->lua_chunk);
/* if file is not set, we were parsing a string */
if (parser->file) {
if ((parser->data != NULL) && (parser->data != MAP_FAILED))
munmap(parser->data, parser->size);
if (parser->fd >= 0)
close(parser->fd);
}
free(parser);
......@@ -154,18 +170,14 @@ static void template_text(struct template_parser *parser, const char *e)
{
const char *s = parser->off;
if (s < (parser->data + parser->size))
{
if (parser->strip_after)
{
while ((s <= e) && isspace(*s))
if (s < (parser->data + parser->size)) {
if (parser->strip_after) {
while ((s < e) && isspace(s[0]))
s++;
}
parser->cur_chunk.type = T_TYPE_TEXT;
}
else
{
} else {
parser->cur_chunk.type = T_TYPE_EOF;
}
......@@ -178,23 +190,20 @@ static void template_code(struct template_parser *parser, const char *e)
{
const char *s = parser->off;
parser->strip_before = 0;
parser->strip_after = 0;
parser->strip_before = false;
parser->strip_after = false;
if (*s == '-')
{
parser->strip_before = 1;
for (s++; (s <= e) && (*s == ' ' || *s == '\t'); s++);
if (s < e && s[0] == '-') {
parser->strip_before = true;
s++;
}
if (*(e-1) == '-')
{
parser->strip_after = 1;
for (e--; (e >= s) && (*e == ' ' || *e == '\t'); e--);
if (s < e && e[-1] == '-') {
parser->strip_after = true;
e--;
}
switch (*s)
{
switch (*s) {
/* comment */
case '#':
s++;
......@@ -220,15 +229,20 @@ static void template_code(struct template_parser *parser, const char *e)
break;
/* expr */
case '=':
case '|':
s++;
parser->cur_chunk.type = T_TYPE_EXPR;
break;
/* expr raw */
case '=':
s++;
parser->cur_chunk.type = T_TYPE_EXPR_RAW;
break;
/* code */
default:
parser->cur_chunk.type = T_TYPE_CODE;
break;
}
parser->cur_chunk.line = parser->line;
......@@ -236,133 +250,118 @@ static void template_code(struct template_parser *parser, const char *e)
parser->cur_chunk.e = e;
}
static const char *
template_format_chunk(struct template_parser *parser, size_t *sz)
static void luastr_escape(struct template_buffer *out, const char *s, const char *e)
{
const char *s, *p;
const char *head, *tail;
struct template_chunk *c = &parser->prv_chunk;
struct template_buffer *buf;
for (const char *ptr = s; ptr < e; ptr++) {
switch (*ptr) {
case '\\':
buf_append(out, "\\\\", 2);
break;
*sz = 0;
s = parser->gc = NULL;
case '\'':
buf_append(out, "\\\'", 2);
break;
if (parser->strip_before && c->type == T_TYPE_TEXT)
case '\n':
buf_append(out, "\\n", 2);
break;
default:
buf_putchar(out, *ptr);
}
}
}
static struct template_buffer * template_format_chunk(struct template_parser *parser)
{
while ((c->e > c->s) && isspace(*(c->e - 1)))
const char *p;
const char *head, *tail;
struct template_chunk *c = &parser->prv_chunk;
if (parser->strip_before && c->type == T_TYPE_TEXT) {
while ((c->e > c->s) && isspace(c->e[-1]))
c->e--;
}
/* empty chunk */
if (c->s == c->e)
{
if (c->type == T_TYPE_EOF)
{
*sz = 0;
s = NULL;
}
else
{
*sz = 1;
s = " ";
}
}
return NULL;
/* format chunk */
else if ((buf = buf_init(c->e - c->s)) != NULL)
{
struct template_buffer *buf = buf_init(c->e - c->s);
if (!buf)
return NULL;
if (c->e > c->s) {
if ((head = gen_code[c->type][0]) != NULL)
buf_append(buf, head, strlen(head));
switch (c->type)
{
switch (c->type) {
case T_TYPE_TEXT:
luastr_escape(buf, c->s, c->e - c->s, 0);
case T_TYPE_INCLUDE:
case T_TYPE_I18N:
case T_TYPE_I18N_RAW:
luastr_escape(buf, c->s, c->e);
break;
case T_TYPE_EXPR:
case T_TYPE_EXPR_RAW:
buf_append(buf, c->s, c->e - c->s);
for (p = c->s; p < c->e; p++)
parser->line += (*p == '\n');
break;
case T_TYPE_INCLUDE:
luastr_escape(buf, c->s, c->e - c->s, 0);
break;
case T_TYPE_I18N:
luastr_translate(buf, c->s, c->e - c->s, 1);
break;
case T_TYPE_I18N_RAW:
luastr_translate(buf, c->s, c->e - c->s, 0);
break;
case T_TYPE_CODE:
buf_append(buf, c->s, c->e - c->s);
for (p = c->s; p < c->e; p++)
parser->line += (*p == '\n');
break;
case T_TYPE_INIT:
case T_TYPE_COMMENT:
case T_TYPE_EOF:
break;
}
if ((tail = gen_code[c->type][1]) != NULL)
buf_append(buf, tail, strlen(tail));
*sz = buf_length(buf);
s = parser->gc = buf_destroy(buf);
if (!*sz)
{
*sz = 1;
s = " ";
}
}
return s;
return buf;
}
const char *template_reader(lua_State *L, void *ud, size_t *sz)
const char * template_reader(lua_State *L __attribute__((unused)), void *ud, size_t *sz)
{
struct template_parser *parser = ud;
/* free previous chunk */
free(parser->lua_chunk);
parser->lua_chunk = NULL;
while (true) {
int rem = parser->size - (parser->off - parser->data);
char *tag;
parser->prv_chunk = parser->cur_chunk;
/* free previous string */
if (parser->gc)
{
free(parser->gc);
parser->gc = NULL;
}
/* before tag */
if (!parser->in_expr)
{
if ((tag = strfind(parser->off, rem, "<%", 2)) != NULL)
{
if (!parser->in_expr) {
if ((tag = memmem(parser->off, rem, "<%", 2)) != NULL) {
template_text(parser, tag);
parser->off = tag + 2;
parser->in_expr = 1;
}
else
{
} else {
template_text(parser, parser->data + parser->size);
parser->off = parser->data + parser->size;
}
}
/* inside tag */
else
{
if ((tag = strfind(parser->off, rem, "%>", 2)) != NULL)
{
else {
if ((tag = memmem(parser->off, rem, "%>", 2)) != NULL) {
template_code(parser, tag);
parser->off = tag + 2;
parser->in_expr = 0;
}
else
{
} else {
/* unexpected EOF */
template_code(parser, parser->data + parser->size);
......@@ -371,7 +370,16 @@ const char *template_reader(lua_State *L, void *ud, size_t *sz)
}
}
return template_format_chunk(parser, sz);
struct template_buffer *buf = template_format_chunk(parser);
if (!buf)
return NULL;
*sz = buf_length(buf);
if (*sz) {
parser->lua_chunk = buf_destroy(buf);
return parser->lua_chunk;
}
}
}
int template_error(lua_State *L, struct template_parser *parser)
......@@ -383,33 +391,30 @@ int template_error(lua_State *L, struct template_parser *parser)
int line = 0;
int chunkline = 0;
if ((ptr = strfind((char *)err, strlen(err), "]:", 2)) != NULL)
{
if ((ptr = memmem(err, strlen(err), "]:", 2)) != NULL) {
chunkline = atoi(ptr + 2) - parser->prv_chunk.line;
while (*ptr)
{
if (*ptr++ == ' ')
{
while (*ptr) {
if (*ptr++ == ' ') {
err = ptr;
break;
}
}
}
if (strfind((char *)err, strlen(err), "'char(27)'", 10) != NULL)
{
if (memmem(err, strlen(err), "'char(27)'", 10) != NULL) {
off = parser->data + parser->size;
err = "'%>' expected before end of file";
chunkline = 0;
}
for (ptr = parser->data; ptr < off; ptr++)
for (ptr = parser->data; ptr < off; ptr++) {
if (*ptr == '\n')
line++;
}
snprintf(msg, sizeof(msg), "Syntax error in %s:%d: %s",
parser->file ? parser->file : "[string]", line + chunkline, err ? err : "(unknown error)");
parser->file ?: "[string]", line + chunkline, err ?: "(unknown error)");
lua_pushnil(L);
lua_pushinteger(L, line + chunkline);
......
/*
* LuCI Template - Parser header
* gluon-web Template - Parser header
*
* Copyright (C) 2009 Jo-Philipp Wich <jow@openwrt.org>
* Copyright (C) 2018 Matthias Schiffer <mschiffer@universe-factory.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -19,59 +20,14 @@
#ifndef _TEMPLATE_PARSER_H_
#define _TEMPLATE_PARSER_H_
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
/* code types */
#define T_TYPE_INIT 0
#define T_TYPE_TEXT 1
#define T_TYPE_COMMENT 2
#define T_TYPE_EXPR 3
#define T_TYPE_INCLUDE 4
#define T_TYPE_I18N 5
#define T_TYPE_I18N_RAW 6
#define T_TYPE_CODE 7
#define T_TYPE_EOF 8
struct template_chunk {
const char *s;
const char *e;
int type;
int line;
};
struct template_parser;
/* parser state */
struct template_parser {
int fd;
uint32_t size;
char *data;
char *off;
char *gc;
int line;
int in_expr;
int strip_before;
int strip_after;
struct template_chunk prv_chunk;
struct template_chunk cur_chunk;
const char *file;
};
struct template_parser * template_open(const char *file);
struct template_parser * template_string(const char *str, uint32_t len);
struct template_parser * template_string(const char *str, size_t len);
void template_close(struct template_parser *parser);
const char *template_reader(lua_State *L, void *ud, size_t *sz);
......
/*
* LuCI Template - Utility functions
* gluon-web Template - Utility functions
*
* Copyright (C) 2010 Jo-Philipp Wich <jow@openwrt.org>
* Copyright (C) 2018 Matthias Schiffer <mschiffer@universe-factory.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -19,29 +20,22 @@
#include "template_utils.h"
#include "template_lmo.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* initialize a buffer object */
struct template_buffer * buf_init(int size)
struct template_buffer * buf_init(size_t size)
{
struct template_buffer *buf;
if (size <= 0)
size = 1024;
struct template_buffer *buf = malloc(sizeof(*buf));
buf = (struct template_buffer *)malloc(sizeof(struct template_buffer));
if (buf != NULL)
{
buf->fill = 0;
if (buf != NULL) {
buf->size = size;
buf->data = malloc(buf->size);
if (buf->data != NULL)
{
buf->dptr = buf->data;
buf->data[0] = 0;
if (buf->data != NULL || size == 0)
return buf;
}
free(buf);
}
......@@ -50,57 +44,48 @@ struct template_buffer * buf_init(int size)
}
/* grow buffer */
static int buf_grow(struct template_buffer *buf, int size)
static bool buf_grow(struct template_buffer *buf, size_t len)
{
unsigned int off = (buf->dptr - buf->data);
char *data;
size_t off = buf->dptr - buf->data, left = buf->size - off;
if (len <= left)
return true;
if (size <= 0)
size = 1024;
size_t diff = len - left;
if (diff < 1024)
diff = 1024;
data = realloc(buf->data, buf->size + size);
char *data = realloc(buf->data, buf->size + diff);
if (data == NULL)
return false;
if (data != NULL)
{
buf->data = data;
buf->dptr = data + off;
buf->size += size;
buf->size += diff;
return buf->size;
}
return 0;
return true;
}
/* put one char into buffer object */
static int buf_putchar(struct template_buffer *buf, char c)
bool buf_putchar(struct template_buffer *buf, char c)
{
if( ((buf->fill + 1) >= buf->size) && !buf_grow(buf, 0) )
return 0;
if (!buf_grow(buf, 1))
return false;
*(buf->dptr++) = c;
*(buf->dptr) = 0;
buf->fill++;
return 1;
return true;
}
/* append data to buffer */
int buf_append(struct template_buffer *buf, const char *s, int len)
{
if ((buf->fill + len + 1) >= buf->size)
bool buf_append(struct template_buffer *buf, const char *s, size_t len)
{
if (!buf_grow(buf, len + 1))
return 0;
}
if (!buf_grow(buf, len))
return false;
memcpy(buf->dptr, s, len);
buf->fill += len;
buf->dptr += len;
*(buf->dptr) = 0;
return len;
return true;
}
/* destroy buffer object and return pointer to data */
......@@ -114,7 +99,7 @@ char * buf_destroy(struct template_buffer *buf)
/* calculate the number of expected continuation chars */
static inline int mb_num_chars(unsigned char c)
static inline size_t mb_num_chars(unsigned char c)
{
if ((c & 0xE0) == 0xC0)
return 2;
......@@ -131,14 +116,14 @@ static inline int mb_num_chars(unsigned char c)
}
/* test whether the given byte is a valid continuation char */
static inline int mb_is_cont(unsigned char c)
static inline bool mb_is_cont(unsigned char c)
{
return ((c >= 0x80) && (c <= 0xBF));
}
/* test whether the byte sequence at the given pointer with the given
* length is the shortest possible representation of the code point */
static inline int mb_is_shortest(unsigned char *s, int n)
static inline bool mb_is_shortest(const unsigned char *s, size_t n)
{
switch (n)
{
......@@ -178,19 +163,19 @@ static inline int mb_is_shortest(unsigned char *s, int n)
((*(s+5) >> 6) == 0x02));
}
return 1;
return true;
}
/* test whether the byte sequence at the given pointer with the given
* length is an UTF-16 surrogate */
static inline int mb_is_surrogate(unsigned char *s, int n)
static inline bool mb_is_surrogate(const unsigned char *s, size_t n)
{
return ((n == 3) && (*s == 0xED) && (*(s+1) >= 0xA0) && (*(s+1) <= 0xBF));
}
/* test whether the byte sequence at the given pointer with the given
* length is an illegal UTF-8 code point */
static inline int mb_is_illegal(unsigned char *s, int n)
static inline bool mb_is_illegal(const unsigned char *s, size_t n)
{
return ((n == 3) && (*s == 0xEF) && (*(s+1) == 0xBF) &&
(*(s+2) >= 0xBE) && (*(s+2) <= 0xBF));
......@@ -199,14 +184,13 @@ static inline int mb_is_illegal(unsigned char *s, int n)
/* scan given source string, validate UTF-8 sequence and store result
* in given buffer object */
static int validate_utf8(unsigned char **s, int l, struct template_buffer *buf)
static size_t validate_utf8(const unsigned char **s, size_t l, struct template_buffer *buf)
{
unsigned char *ptr = *s;
unsigned int o = 0, v, n;
const unsigned char *ptr = *s;
size_t o = 0, v, n;
/* ascii byte without null */
if ((*(ptr+0) >= 0x01) && (*(ptr+0) <= 0x7F))
{
if ((*(ptr+0) >= 0x01) && (*(ptr+0) <= 0x7F)) {
if (!buf_putchar(buf, *ptr++))
return 0;
......@@ -214,8 +198,7 @@ static int validate_utf8(unsigned char **s, int l, struct template_buffer *buf)
}
/* multi byte sequence */
else if ((n = mb_num_chars(*ptr)) > 1)
{
else if ((n = mb_num_chars(*ptr)) > 1) {
/* count valid chars */
for (v = 1; (v <= n) && ((o+v) < l) && mb_is_cont(*(ptr+v)); v++);
......@@ -237,7 +220,7 @@ static int validate_utf8(unsigned char **s, int l, struct template_buffer *buf)
!mb_is_surrogate(ptr, n) && !mb_is_illegal(ptr, n))
{
/* copy sequence */
if (!buf_append(buf, (char *)ptr, n))
if (!buf_append(buf, (const char *)ptr, n))
return 0;
}
......@@ -252,14 +235,13 @@ static int validate_utf8(unsigned char **s, int l, struct template_buffer *buf)
break;
}
/* advance beyound the last found valid continuation char */
/* advance beyond the last found valid continuation char */
o = v;
ptr += v;
}
/* invalid byte (0x00) */
else
{
else {
if (!buf_putchar(buf, '?')) /* or 0xEF, 0xBF, 0xBD */
return 0;
......@@ -274,35 +256,32 @@ static int validate_utf8(unsigned char **s, int l, struct template_buffer *buf)
/* Sanitize given string and strip all invalid XML bytes
* Validate UTF-8 sequences
* Escape XML control chars */
char * pcdata(const char *s, unsigned int l)
bool pcdata(const char *s, size_t l, char **out, size_t *outl)
{
struct template_buffer *buf = buf_init(l);
unsigned char *ptr = (unsigned char *)s;
unsigned int o, v;
const unsigned char *ptr = (const unsigned char *)s;
size_t o, v;
char esq[8];
int esl;
if (!buf)
return NULL;
return false;
for (o = 0; o < l; o++)
{
for (o = 0; o < l; o++) {
/* Invalid XML bytes */
if (((*ptr >= 0x00) && (*ptr <= 0x08)) ||
if ((*ptr <= 0x08) ||
((*ptr >= 0x0B) && (*ptr <= 0x0C)) ||
((*ptr >= 0x0E) && (*ptr <= 0x1F)) ||
(*ptr == 0x7F))
{
(*ptr == 0x7F)) {
ptr++;
}
/* Escapes */
else if ((*ptr == 0x26) ||
(*ptr == 0x27) ||
(*ptr == 0x22) ||
(*ptr == 0x3C) ||
(*ptr == 0x3E))
{
else if ((*ptr == '\'') ||
(*ptr == '"') ||
(*ptr == '&') ||
(*ptr == '<') ||
(*ptr == '>')) {
esl = snprintf(esq, sizeof(esq), "&#%i;", *ptr);
if (!buf_append(buf, esq, esl))
......@@ -312,14 +291,12 @@ char * pcdata(const char *s, unsigned int l)
}
/* ascii char */
else if (*ptr <= 0x7F)
{
else if (*ptr <= 0x7F) {
buf_putchar(buf, (char)*ptr++);
}
/* multi byte sequence */
else
{
else {
if (!(v = validate_utf8(&ptr, l - o, buf)))
break;
......@@ -327,58 +304,7 @@ char * pcdata(const char *s, unsigned int l)
}
}
return buf_destroy(buf);
}
void luastr_escape(struct template_buffer *out, const char *s, unsigned int l, int escape_xml)
{
int esl;
char esq[8];
char *ptr;
for (ptr = (char *)s; ptr < (s + l); ptr++)
{
switch (*ptr)
{
case '\\':
buf_append(out, "\\\\", 2);
break;
case '"':
if (escape_xml)
buf_append(out, "&#34;", 5);
else
buf_append(out, "\\\"", 2);
break;
case '\n':
buf_append(out, "\\n", 2);
break;
case '\'':
case '&':
case '<':
case '>':
if (escape_xml)
{
esl = snprintf(esq, sizeof(esq), "&#%i;", *ptr);
buf_append(out, esq, esl);
break;
}
default:
buf_putchar(out, *ptr);
}
}
}
void luastr_translate(struct template_buffer *out, const char *s, unsigned int l, int escape_xml)
{
char *tr;
int trlen;
if (!lmo_translate(s, l, &tr, &trlen))
luastr_escape(out, tr, trlen, escape_xml);
else
luastr_escape(out, s, l, escape_xml);
*outl = buf_length(buf);
*out = buf_destroy(buf);
return true;
}
/*
* LuCI Template - Utility header
* gluon-web Template - Utility header
*
* Copyright (C) 2010-2012 Jo-Philipp Wich <jow@openwrt.org>
* Copyright (C) 2018 Matthias Schiffer <mschiffer@universe-factory.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -19,33 +20,28 @@
#ifndef _TEMPLATE_UTILS_H_
#define _TEMPLATE_UTILS_H_
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stddef.h>
/* buffer object */
struct template_buffer {
char *data;
char *dptr;
unsigned int size;
unsigned int fill;
size_t size;
};
struct template_buffer * buf_init(int size);
int buf_append(struct template_buffer *buf, const char *s, int len);
struct template_buffer * buf_init(size_t size);
bool buf_putchar(struct template_buffer *buf, char c);
bool buf_append(struct template_buffer *buf, const char *s, size_t len);
char * buf_destroy(struct template_buffer *buf);
/* read buffer length */
static inline int buf_length(struct template_buffer *buf)
static inline size_t buf_length(const struct template_buffer *buf)
{
return buf->fill;
return buf->dptr - buf->data;
}
char * pcdata(const char *s, unsigned int l);
void luastr_escape(struct template_buffer *out, const char *s, unsigned int l, int escape_xml);
void luastr_translate(struct template_buffer *out, const char *s, unsigned int l, int escape_xml);
bool pcdata(const char *s, size_t l, char **out, size_t *outl);
#endif
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-wireless-encryption-wpa3
include ../gluon.mk
define Package/gluon-wireless-encryption-wpa3
DEPENDS:=+hostapd-mbedtls
TITLE:=Package for supporting WPA3 encrypted wireless networks
endef
define Package/gluon-wireless-encryption-wpa3/install
$(INSTALL_DIR) $(1)/lib/gluon/features
touch $(1)/lib/gluon/features/wpa2
touch $(1)/lib/gluon/features/wpa3
endef
$(eval $(call BuildPackageGluon,gluon-wireless-encryption-wpa3))
GLUON_MK := $(abspath $(lastword $(MAKEFILE_LIST)))
PKG_FILE_DEPENDS += $(GLUON_MK)
# Dependencies for LuaSrcDiet
PKG_BUILD_DEPENDS += luci-base/host
PKG_VERSION ?= 1
PKG_BUILD_DEPENDS += luasrcdiet/host
ifneq ($(wildcard ./luasrc/.),)
PKG_CONFIG_DEPENDS += CONFIG_GLUON_MINIFY
endif
ifneq ($(wildcard ./src/respondd.c),)
PKG_BUILD_DEPENDS += respondd
endif
include $(INCLUDE_DIR)/package.mk
......@@ -14,7 +23,8 @@ shell-verbatim = $(call shell-unescape,$(call shell-escape,$(1)))
define GluonCheckSite
[ -z "$$IPKG_INSTROOT" ] || "${TOPDIR}/staging_dir/hostpkg/bin/lua" "${TOPDIR}/../scripts/check_site.lua" <<'END__GLUON__CHECK__SITE'
[ -z "$$STAGING_DIR_HOSTPKG" ] || PATH="$$STAGING_DIR_HOSTPKG/bin:$$PATH"
lua "$$IPKG_INSTROOT/lib/gluon/check-site.lua" <<'END__GLUON__CHECK__SITE'
$(call shell-verbatim,cat '$(1)')
END__GLUON__CHECK__SITE
endef
......@@ -26,22 +36,27 @@ GLUON_LANG_fr := French
GLUON_I18N_CONFIG := $(foreach lang,$(GLUON_SUPPORTED_LANGS),CONFIG_GLUON_WEB_LANG_$(lang))
GLUON_ENABLED_LANGS := en $(foreach lang,$(GLUON_SUPPORTED_LANGS),$(if $(CONFIG_GLUON_WEB_LANG_$(lang)),$(lang)))
ifneq ($(wildcard ./i18n/.),)
PKG_BUILD_DEPENDS += gluon-web/host
PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
endif
define GluonBuildI18N
mkdir -p $$(PKG_BUILD_DIR)/i18n
for lang in $$(GLUON_ENABLED_LANGS); do \
if [ -e $(2)/$$$$lang.po ]; then \
rm -f $$(PKG_BUILD_DIR)/i18n/$(1).$$$$lang.lmo; \
po2lmo $(2)/$$$$lang.po $$(PKG_BUILD_DIR)/i18n/$(1).$$$$lang.lmo; \
if [ -e $(1)/$$$$lang.po ]; then \
rm -f $$(PKG_BUILD_DIR)/i18n/$$$$lang.lmo; \
gluon-po2lmo $(1)/$$$$lang.po $$(PKG_BUILD_DIR)/i18n/$$$$lang.lmo; \
fi; \
done
endef
define GluonInstallI18N
$$(INSTALL_DIR) $(2)/lib/gluon/web/i18n
$$(INSTALL_DIR) $(1)/lib/gluon/web/i18n
for lang in $$(GLUON_ENABLED_LANGS); do \
if [ -e $$(PKG_BUILD_DIR)/i18n/$(1).$$$$lang.lmo ]; then \
$$(INSTALL_DATA) $$(PKG_BUILD_DIR)/i18n/$(1).$$$$lang.lmo $(2)/lib/gluon/web/i18n/$(1).$$$$lang.lmo; \
if [ -e $$(PKG_BUILD_DIR)/i18n/$$$$lang.lmo ]; then \
$$(INSTALL_DATA) $$(PKG_BUILD_DIR)/i18n/$$$$lang.lmo $(1)/lib/gluon/web/i18n/$(PKG_NAME).$$$$lang.lmo; \
fi; \
done
endef
......@@ -49,10 +64,66 @@ endef
define GluonSrcDiet
rm -rf $(2)
$(CP) $(1) $(2)
$(FIND) $(2) -type f | while read src; do \
if LuaSrcDiet --noopt-binequiv -o "$$$$src.o" "$$$$src"; then \
chmod $$$$(stat -c%a "$$$$src") "$$$$src.o"; \
mv "$$$$src.o" "$$$$src"; \
fi; \
ifdef CONFIG_GLUON_MINIFY
# Use cp + rm instead of mv to preserve destination permissions
set -e; $(FIND) $(2) -type f | while read src; do \
echo "Minifying $$$$src..."; \
luasrcdiet --noopt-binequiv -o "$$$$src.tmp" "$$$$src"; \
cp "$$$$src.tmp" "$$$$src"; \
rm "$$$$src.tmp"; \
done
endif
endef
define Gluon/Build/Compile
$(if $(wildcard ./src/Makefile ./src/CMakeLists.txt),
$(Build/Compile/Default)
)
$(if $(wildcard ./luasrc/.),
$(call GluonSrcDiet,luasrc,$(PKG_BUILD_DIR)/luadest/)
)
$(if $(wildcard ./i18n/.),
$(call GluonBuildI18N,i18n)
)
endef
define Gluon/Build/Install
$(if $(findstring $(PKG_INSTALL),1),
$(CP) $(PKG_INSTALL_DIR)/. $(1)/
)
$(if $(wildcard ./files/.),
$(CP) ./files/. $(1)/
)
$(if $(wildcard ./luasrc/.),
$(CP) $(PKG_BUILD_DIR)/luadest/. $(1)/
)
$(if $(wildcard ./src/respondd.c),
$(INSTALL_DIR) $(1)/usr/lib/respondd
$(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/usr/lib/respondd/$(PKG_NAME).so
)
$(if $(wildcard ./i18n/.),
$(GluonInstallI18N)
)
endef
Build/Compile=$(call Gluon/Build/Compile)
define BuildPackageGluon
define Package/$(1) :=
SECTION:=gluon
CATEGORY:=Gluon
$$(Package/$(1))
endef
Package/$(1)/install ?= $$(Gluon/Build/Install)
ifneq ($(wildcard check_site.lua),)
define Package/$(1)/postinst
#!/bin/sh
$$(call GluonCheckSite,check_site.lua)
endef
endif
$$(eval $$(call BuildPackage,$(1)))
endef
......@@ -28,7 +28,6 @@ PKG_NAME:=libbatadv
PKG_VERSION:=1
PKG_LICENSE:=MIT
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
PKG_INSTALL:=1
include $(INCLUDE_DIR)/package.mk
......